1. Propósito del análisis

El presente informe describe la base de datos de evaluaciones de fuerza isocinética en jugadores de fútbol argentino con reconstrucción del ligamento cruzado anterior (RLCA).

El análisis se planteó con un enfoque descriptivo, orientado a:

  1. Depurar y organizar la base de datos.
  2. Definir una cohorte analítica consistente.
  3. Caracterizar a los jugadores y las evaluaciones incluidas.
  4. Describir los valores de fuerza isocinética según nivel competitivo y velocidad de evaluación.
  5. Generar tablas y figuras utilizables para la redacción de resultados.

No se realizaron comparaciones inferenciales ni pruebas de hipótesis en esta etapa, dado que el objetivo principal del trabajo es descriptivo.


2. Carga de paquetes y configuración general

# ============================================================
# 0) PAQUETES
# ============================================================

paquetes <- c(
  "readxl",
  "dplyr",
  "tidyr",
  "stringr",
  "stringi",
  "lubridate",
  "readr",
  "janitor",
  "ggplot2",
  "naniar",
  "visdat",
  "skimr",
  "purrr",
  "forcats",
  "flextable",
  "officer"
)

instalar <- paquetes[!paquetes %in% rownames(installed.packages())]

if(length(instalar) > 0){
  install.packages(instalar)
}

invisible(lapply(paquetes, library, character.only = TRUE))

3. Carga de la base original

La base se carga desde un archivo Excel. Todas las columnas se importan inicialmente como texto para evitar problemas derivados de:

  • fechas guardadas en formatos distintos;
  • celdas con la leyenda “Dato faltante”;
  • columnas numéricas mezcladas con texto;
  • diferencias de codificación entre categorías.
# ============================================================
# 1) CARGA DE LA BASE
# ============================================================

ruta <- "C:/Users/Juan_Cruz/Desktop/Cosas Juan kinesiologia/Fuerza isocinetica futbol argentino/Isocinesia_RLCA_Analizar.xlsx"

# Leemos primero solo encabezados para conocer la cantidad de columnas
n_columnas <- readxl::read_excel(
  path = ruta,
  sheet = "Datos Isocinesia",
  n_max = 0
) %>%
  ncol()

# Cargamos TODO como texto
datos_raw <- readxl::read_excel(
  path = ruta,
  sheet = "Datos Isocinesia",
  col_types = rep("text", n_columnas)
) %>%
  janitor::clean_names()

names(datos_raw)
 [1] "nombre"                                                     
 [2] "id"                                                         
 [3] "nacido"                                                     
 [4] "altura"                                                     
 [5] "peso"                                                       
 [6] "sexo"                                                       
 [7] "sesion"                                                     
 [8] "edad_al_momento_de_evaluar"                                 
 [9] "implicado"                                                  
[10] "terapeuta"                                                  
[11] "referente"                                                  
[12] "articulacion"                                               
[13] "nivel_de_competencia"                                       
[14] "momento_de_evaluacion_pop_en_meses"                         
[15] "categorias_momento_de_evaluacion_menor_a_6m_6_8m_mayor_a_8m"
[16] "diagnostico"                                                
[17] "injerto"                                                    
[18] "lemaire_refuerzo_lateral"                                   
[19] "relesion"                                                   
[20] "velocidad"                                                  
[21] "pico_del_par_extension_no_implicado"                        
[22] "pico_del_par_extension_implicado"                           
[23] "pico_del_par_flexion_no_implicado"                          
[24] "pico_del_par_flexion_implicado"                             
[25] "n_rep_trabajo_max_extension_no_implicado"                   
[26] "n_rep_trabajo_max_extension_implicado"                      
[27] "n_rep_trabajo_max_flexion_no_implicado"                     
[28] "n_rep_trabajo_max_flexion_implicado"                        
[29] "trabj_peso_corporal_extension_no_implicado"                 
[30] "trabj_peso_corporal_extension_implicado"                    
[31] "trabj_peso_corporal_flexion_no_implicado"                   
[32] "trabj_peso_corporal_flexion_implicado"                      
[33] "trabajo_total_extension_no_implicado"                       
[34] "trabajo_total_extension_implicado"                          
[35] "trabajo_total_flexion_no_implicado"                         
[36] "trabajo_total_flexion_implicado"                            
[37] "rango_de_mov_extension_no_implicado"                        
[38] "rango_de_mov_extension_implicado"                           
[39] "rango_de_mov_flexion_no_implicado"                          
[40] "rango_de_mov_flexion_implicado"                             
[41] "razon_agon_antagon_extension_no_implicado"                  
[42] "razon_agon_antagon_extension_implicado"                     
[43] "razon_agon_antagon_flexion_no_implicado"                    
[44] "razon_agon_antagon_flexion_implicado"                       
[45] "simetria_pico_del_par_extension_percent"                    
[46] "simetria_pico_del_par_flexion_percent"                      
[47] "simetria_trabj_peso_corporal_extension_percent"             
[48] "simetria_trabj_peso_corporal_flexion_percent"               
[49] "simetria_trabajo_total_extension_percent"                   
[50] "simetria_trabajo_total_flexion_percent"                     

4. Renombrado de columnas

Se acortan los nombres de dos variables para facilitar su uso posterior.

# ============================================================
# 2) RENOMBRAR COLUMNAS CLAVE
# ============================================================

names(datos_raw)[names(datos_raw) == "momento_de_evaluacion_pop_en_meses"] <- 
  "momento_pop_meses"

names(datos_raw)[str_detect(
  names(datos_raw),
  "^categorias_momento_de_evaluacion"
)] <- "categoria_momento_pop_original"

5. Funciones auxiliares para limpieza y transformación

Se definen funciones para:

  • reconocer faltantes;
  • estandarizar texto;
  • transformar fechas;
  • convertir números;
  • normalizar variables categóricas;
  • calcular simetrías entre miembro implicado y no implicado.
# ============================================================
# 3) FUNCIONES AUXILIARES DE LIMPIEZA
# ============================================================

limpiar_faltantes <- function(x){
  
  x <- as.character(x)
  x <- stringr::str_squish(x)
  
  x[x == ""] <- NA_character_
  
  x_normalizado <- x %>%
    stringr::str_to_lower() %>%
    stringi::stri_trans_general("Latin-ASCII")
  
  x[x_normalizado %in% c(
    "dato faltante",
    "datos faltantes",
    "sin dato",
    "sin datos",
    "na",
    "n/a",
    "nd",
    "s/d",
    "null"
  )] <- NA_character_
  
  return(x)
}


normalizar_texto <- function(x){
  
  x <- limpiar_faltantes(x)
  
  x %>%
    stringr::str_squish() %>%
    stringr::str_to_lower() %>%
    stringi::stri_trans_general("Latin-ASCII")
}


parse_fecha_mixta <- function(x){
  
  x <- limpiar_faltantes(x)
  x_num <- suppressWarnings(as.numeric(x))
  
  salida <- rep(as.Date(NA), length(x))
  
  idx_excel <- !is.na(x_num) & x_num > 20000 & x_num < 80000
  
  salida[idx_excel] <- as.Date(
    x_num[idx_excel],
    origin = "1899-12-30"
  )
  
  idx_texto <- !idx_excel & !is.na(x)
  
  salida[idx_texto] <- suppressWarnings(
    lubridate::mdy(x[idx_texto])
  )
  
  return(salida)
}


parse_num <- function(x){
  
  readr::parse_number(
    limpiar_faltantes(x),
    locale = readr::locale(
      decimal_mark = ".",
      grouping_mark = ","
    )
  )
}


normalizar_si_no <- function(x){
  
  x_norm <- normalizar_texto(x)
  
  salida <- dplyr::case_when(
    is.na(x_norm) ~ NA_character_,
    x_norm %in% c("si", "s", "yes", "y", "1") ~ "Sí",
    x_norm %in% c("no", "n", "0") ~ "No",
    TRUE ~ NA_character_
  )
  
  factor(salida, levels = c("No", "Sí"))
}


normalizar_implicado <- function(x){
  
  x_norm <- normalizar_texto(x)
  
  salida <- dplyr::case_when(
    is.na(x_norm) ~ NA_character_,
    x_norm == "derecho" ~ "Derecho",
    x_norm == "izquierdo" ~ "Izquierdo",
    TRUE ~ NA_character_
  )
  
  factor(salida, levels = c("Derecho", "Izquierdo"))
}


normalizar_injerto <- function(x){
  
  x_norm <- normalizar_texto(x)
  
  salida <- dplyr::case_when(
    is.na(x_norm) ~ NA_character_,
    x_norm == "cuadricipital" ~ "Cuadricipital",
    x_norm == "hth" ~ "HTH",
    x_norm == "stri" ~ "STRI",
    TRUE ~ NA_character_
  )
  
  factor(
    salida,
    levels = c("HTH", "STRI", "Cuadricipital")
  )
}


normalizar_categoria_momento <- function(x){
  
  x_norm <- normalizar_texto(x)
  
  salida <- dplyr::case_when(
    is.na(x_norm) ~ NA_character_,
    str_detect(x_norm, "menor") & str_detect(x_norm, "6") ~ "<6 meses",
    str_detect(x_norm, "6") & str_detect(x_norm, "8") ~ "6-8 meses",
    str_detect(x_norm, "mayor") & str_detect(x_norm, "8") ~ ">8 meses",
    TRUE ~ NA_character_
  )
  
  factor(
    salida,
    levels = c("<6 meses", "6-8 meses", ">8 meses")
  )
}


redondear_meses <- function(x){
  floor(x + 0.5)
}


calcular_simetria <- function(valor_implicado, valor_no_implicado){
  
  dplyr::if_else(
    is.na(valor_implicado) |
      is.na(valor_no_implicado) |
      valor_no_implicado == 0,
    NA_real_,
    (valor_implicado / valor_no_implicado) * 100
  )
}

6. Identificación de variables numéricas

Se organizaron las columnas que corresponden a:

  • variables de peso y altura;
  • momento postoperatorio;
  • métricas de fuerza;
  • número de repetición de trabajo máximo.
# ============================================================
# 4) IDENTIFICAR GRUPOS DE VARIABLES NUMÉRICAS
# ============================================================

vars_metricas_continuas <- names(datos_raw)[str_detect(
  names(datos_raw),
  "^(pico_del_par|trabj_peso_corporal|trabajo_total|rango_de_mov|razon_agon_antagon)"
)]

vars_repeticiones <- names(datos_raw)[str_detect(
  names(datos_raw),
  "rep_trabajo_max"
)]

vars_numericas <- unique(c(
  "altura",
  "peso",
  "momento_pop_meses",
  vars_metricas_continuas,
  vars_repeticiones
))

7. Limpieza de base de datos

En esta etapa se:

  • eliminaron columnas no utilizadas en el análisis;
  • conversion de variables numéricas;
  • transformación de fechas;
  • calculo de edad al momento de la evaluación;
  • normalización de variables categorías;
  • generación de las categorías temporales del momento postquirúrgico.

Las ventanas temporales fueron:

  • <6 meses
  • 6 a <8 meses
  • ≥8 meses
# ============================================================
# 5) CONSTRUIR BASE LIMPIA
# ============================================================

datos_iso <- datos_raw %>%
  
  mutate(across(everything(), limpiar_faltantes)) %>%
  
  select(
    -any_of(c(
      "nombre",
      "sexo",
      "terapeuta",
      "referente",
      "articulacion",
      "diagnostico",
      "edad_al_momento_de_evaluar"
    ))
  ) %>%
  
  mutate(
    across(
      all_of(intersect(vars_numericas, names(.))),
      parse_num
    )
  ) %>%
  
  mutate(
    
    id = as.character(id),
    
    nacido = parse_fecha_mixta(nacido),
    sesion = parse_fecha_mixta(sesion),
    
    edad_evaluacion = if_else(
      is.na(nacido) | is.na(sesion),
      NA_integer_,
      as.integer(
        floor(
          lubridate::time_length(
            lubridate::interval(nacido, sesion),
            unit = "years"
          )
        )
      )
    ),
    
    momento_pop_meses_redondeado = redondear_meses(momento_pop_meses),
    
    implicado = normalizar_implicado(implicado),
    nivel_de_competencia = factor(nivel_de_competencia),
    categoria_momento_pop_original = normalizar_categoria_momento(
      categoria_momento_pop_original
    ),
    injerto = normalizar_injerto(injerto),
    lemaire_refuerzo_lateral = normalizar_si_no(lemaire_refuerzo_lateral),
    relesion = normalizar_si_no(relesion),
    velocidad = factor(velocidad),
    
    categoria_momento_pop_derivada = factor(
      case_when(
        is.na(momento_pop_meses) ~ NA_character_,
        momento_pop_meses < 6 ~ "<6 meses",
        momento_pop_meses >= 6 & momento_pop_meses < 8 ~ "6 a <8 meses",
        momento_pop_meses >= 8 ~ "≥8 meses"
      ),
      levels = c("<6 meses", "6 a <8 meses", "≥8 meses")
    )
  )

8. Cálculo de variables de simetría

Se calcularon índices de simetría de acuerdo con la fórmula:

\[ \text{Simetría} = \frac{\text{valor implicado}}{\text{valor no implicado}} \times 100 \]

Se generaron simetrías para:

  • pico de par en extensión;
  • pico de par en flexión;
  • trabajo/peso corporal en extensión;
  • trabajo/peso corporal en flexión;
  • trabajo total en extensión;
  • trabajo total en flexión.
# ============================================================
# 5.1) CREAR VARIABLES DE SIMETRÍA
# ============================================================

datos_iso <- datos_iso %>%
  mutate(
    
    simetria_pico_par_extension = calcular_simetria(
      valor_implicado = pico_del_par_extension_implicado,
      valor_no_implicado = pico_del_par_extension_no_implicado
    ),
    
    simetria_pico_par_flexion = calcular_simetria(
      valor_implicado = pico_del_par_flexion_implicado,
      valor_no_implicado = pico_del_par_flexion_no_implicado
    ),
    
    simetria_trabj_peso_corporal_extension = calcular_simetria(
      valor_implicado = trabj_peso_corporal_extension_implicado,
      valor_no_implicado = trabj_peso_corporal_extension_no_implicado
    ),
    
    simetria_trabj_peso_corporal_flexion = calcular_simetria(
      valor_implicado = trabj_peso_corporal_flexion_implicado,
      valor_no_implicado = trabj_peso_corporal_flexion_no_implicado
    ),
    
    simetria_trabajo_total_extension = calcular_simetria(
      valor_implicado = trabajo_total_extension_implicado,
      valor_no_implicado = trabajo_total_extension_no_implicado
    ),
    
    simetria_trabajo_total_flexion = calcular_simetria(
      valor_implicado = trabajo_total_flexion_implicado,
      valor_no_implicado = trabajo_total_flexion_no_implicado
    )
  )

vars_simetrias <- c(
  "simetria_pico_par_extension",
  "simetria_pico_par_flexion",
  "simetria_trabj_peso_corporal_extension",
  "simetria_trabj_peso_corporal_flexion",
  "simetria_trabajo_total_extension",
  "simetria_trabajo_total_flexion"
)

9. Revisión general de la base curada

# ============================================================
# 6) VERIFICACIÓN INICIAL DE LA BASE LIMPIA
# ============================================================

glimpse(datos_iso)
Rows: 252
Columns: 52
$ id                                             <chr> "872", "872", "872", "948", "948", "948", "1024",…
$ nacido                                         <date> 1997-09-20, 1997-09-20, 1997-09-20, 1998-01-19, …
$ altura                                         <dbl> 180, 180, 180, 182, 182, 182, 182, 182, 182, 184,…
$ peso                                           <dbl> 76, 76, 76, 76, 76, 76, 76, 76, 76, 80, 80, 80, 8…
$ sesion                                         <date> 2024-12-11, 2024-12-11, 2024-12-11, 2024-10-24, …
$ implicado                                      <fct> Derecho, Derecho, Derecho, Derecho, Derecho, Dere…
$ nivel_de_competencia                           <fct> Primera división, Primera división, Primera divis…
$ momento_pop_meses                              <dbl> 7.0, 7.0, 7.0, 7.5, 7.5, 7.5, 6.5, 6.5, 6.5, 6.0,…
$ categoria_momento_pop_original                 <fct> 6-8 meses, 6-8 meses, 6-8 meses, 6-8 meses, 6-8 m…
$ injerto                                        <fct> NA, NA, NA, NA, NA, NA, STRI, STRI, STRI, NA, NA,…
$ lemaire_refuerzo_lateral                       <fct> No, No, No, No, No, No, No, No, No, No, No, No, N…
$ relesion                                       <fct> Sí, Sí, Sí, Sí, Sí, Sí, Sí, Sí, Sí, Sí, Sí, Sí, S…
$ velocidad                                      <fct> 60°/seg, 180°/seg, 300°/seg, 60°/seg, 180°/seg, 3…
$ pico_del_par_extension_no_implicado            <dbl> 240.4, 175.1, 124.5, 202.3, 153.4, 108.7, 194.4, …
$ pico_del_par_extension_implicado               <dbl> 238.8, 169.3, 118.4, 187.4, 145.0, 108.8, 202.5, …
$ pico_del_par_flexion_no_implicado              <dbl> 163.6, 115.9, 106.3, 161.8, 122.8, 100.6, 127.5, …
$ pico_del_par_flexion_implicado                 <dbl> 162.9, 116.6, 111.1, 149.5, 119.8, 109.4, 137.9, …
$ n_rep_trabajo_max_extension_no_implicado       <dbl> 4, 2, 4, 3, 4, 4, 5, 4, 3, 4, 6, 6, 3, 3, 2, 4, 6…
$ n_rep_trabajo_max_extension_implicado          <dbl> 4, 2, 3, 3, 4, 3, 5, 3, 3, 5, 2, 2, 4, 9, 4, 1, 1…
$ n_rep_trabajo_max_flexion_no_implicado         <dbl> 3, 2, 3, 2, 3, 3, 2, 2, 2, 5, 2, 2, 1, 2, 1, 5, 4…
$ n_rep_trabajo_max_flexion_implicado            <dbl> 4, 1, 1, 2, 5, 3, 4, 4, 3, 2, 3, 6, 2, 4, 2, 1, 2…
$ trabj_peso_corporal_extension_no_implicado     <dbl> 265.9, 224.7, 160.9, 274.7, 225.7, 182.0, 221.9, …
$ trabj_peso_corporal_extension_implicado        <dbl> 298.2, 246.5, 180.2, 262.0, 221.6, 179.4, 260.3, …
$ trabj_peso_corporal_flexion_no_implicado       <dbl> 250.3, 204.4, 159.2, 284.2, 214.3, 167.0, 154.3, …
$ trabj_peso_corporal_flexion_implicado          <dbl> 232.7, 199.5, 156.6, 255.6, 212.1, 163.1, 193.0, …
$ trabajo_total_extension_no_implicado           <dbl> 908.8, 1526.1, 1551.3, 991.9, 1561.2, 1707.7, 778…
$ trabajo_total_extension_implicado              <dbl> 1085.7, 1666.3, 1727.1, 963.9, 1556.9, 1752.2, 81…
$ trabajo_total_flexion_no_implicado             <dbl> 855.3, 1410.0, 1553.7, 1020.6, 1506.3, 1581.8, 56…
$ trabajo_total_flexion_implicado                <dbl> 853.4, 1370.5, 1471.2, 928.2, 1475.9, 1599.8, 664…
$ rango_de_mov_extension_no_implicado            <dbl> 88.7, 88.8, 88.0, 88.0, 88.9, 88.6, 69.7, 89.3, 8…
$ rango_de_mov_extension_implicado               <dbl> 86.4, 89.4, 88.8, 85.8, 89.3, 89.3, 83.1, 88.7, 8…
$ rango_de_mov_flexion_no_implicado              <dbl> 88.7, 88.8, 88.0, 88.0, 88.9, 88.6, 69.7, 89.3, 8…
$ rango_de_mov_flexion_implicado                 <dbl> 86.4, 89.4, 88.8, 85.8, 89.3, 89.3, 83.1, 88.7, 8…
$ razon_agon_antagon_extension_no_implicado      <dbl> 68.1, 66.2, 85.4, 80.0, 80.0, 92.6, 65.6, 62.7, 7…
$ razon_agon_antagon_extension_implicado         <dbl> 68.2, 68.9, 93.8, 79.8, 82.6, 100.6, 68.1, 68.2, …
$ razon_agon_antagon_flexion_no_implicado        <dbl> -12.1, -9.7, -12.0, 4.6, 1.8, 1.4, -17.3, 3.0, -0…
$ razon_agon_antagon_flexion_implicado           <dbl> -19.5, -9.2, -11.3, 2.8, 0.3, -2.6, -5.1, 4.4, 1.…
$ simetria_pico_del_par_extension_percent        <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ simetria_pico_del_par_flexion_percent          <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ simetria_trabj_peso_corporal_extension_percent <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ simetria_trabj_peso_corporal_flexion_percent   <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ simetria_trabajo_total_extension_percent       <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ simetria_trabajo_total_flexion_percent         <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ edad_evaluacion                                <int> 27, 27, 27, 26, 26, 26, 32, 32, 32, 22, 22, 22, 2…
$ momento_pop_meses_redondeado                   <dbl> 7, 7, 7, 8, 8, 8, 7, 7, 7, 6, 6, 6, 6, 6, 6, 10, …
$ categoria_momento_pop_derivada                 <fct> 6 a <8 meses, 6 a <8 meses, 6 a <8 meses, 6 a <8 …
$ simetria_pico_par_extension                    <dbl> 99.33444, 96.68761, 95.10040, 92.63470, 94.52412,…
$ simetria_pico_par_flexion                      <dbl> 99.57213, 100.60397, 104.51552, 92.39802, 97.5570…
$ simetria_trabj_peso_corporal_extension         <dbl> 112.14742, 109.70182, 111.99503, 95.37677, 98.183…
$ simetria_trabj_peso_corporal_flexion           <dbl> 92.96844, 97.60274, 98.36683, 89.93666, 98.97340,…
$ simetria_trabajo_total_extension               <dbl> 119.46523, 109.18682, 111.33243, 97.17713, 99.724…
$ simetria_trabajo_total_flexion                 <dbl> 99.77786, 97.19858, 94.69009, 90.94650, 97.98181,…
summary(datos_iso)
      id                nacido               altura           peso           sesion          
 Length:252         Min.   :1988-10-27   Min.   :168.0   Min.   : 61.0   Min.   :2024-01-11  
 Class :character   1st Qu.:1998-02-19   1st Qu.:175.0   1st Qu.: 73.0   1st Qu.:2025-01-21  
 Mode  :character   Median :2001-01-03   Median :178.0   Median : 76.0   Median :2025-04-09  
                    Mean   :2000-06-22   Mean   :178.6   Mean   : 77.3   Mean   :2025-03-27  
                    3rd Qu.:2003-05-29   3rd Qu.:182.5   3rd Qu.: 80.5   3rd Qu.:2025-07-02  
                    Max.   :2008-03-22   Max.   :194.0   Max.   :110.0   Max.   :2026-03-18  
                    NA's   :9                                                                
     implicado         nivel_de_competencia momento_pop_meses categoria_momento_pop_original
 Derecho  :150   Amateur         : 42       Min.   : 3.000    <6 meses :75                  
 Izquierdo:102   Primera división:117       1st Qu.: 5.375    6-8 meses:78                  
                 Segunda división: 75       Median : 7.000    >8 meses :99                  
                 Tercera División: 18       Mean   : 7.718                                  
                                            3rd Qu.: 9.000                                  
                                            Max.   :26.000                                  
                                                                                            
          injerto    lemaire_refuerzo_lateral relesion    velocidad  pico_del_par_extension_no_implicado
 HTH          : 72   No:231                   No:216   180°/seg:84   Min.   : 91.3                      
 STRI         : 36   Sí: 21                   Sí: 36   300°/seg:84   1st Qu.:137.4                      
 Cuadricipital:  3                                     60°/seg :84   Median :168.2                      
 NA's         :141                                                   Mean   :181.4                      
                                                                     3rd Qu.:222.1                      
                                                                     Max.   :336.5                      
                                                                                                        
 pico_del_par_extension_implicado pico_del_par_flexion_no_implicado pico_del_par_flexion_implicado
 Min.   : 59.3                    Min.   : 45.50                    Min.   : 54.8                 
 1st Qu.:117.5                    1st Qu.: 93.47                    1st Qu.: 91.7                 
 Median :143.7                    Median :111.95                    Median :109.6                 
 Mean   :153.0                    Mean   :113.79                    Mean   :111.0                 
 3rd Qu.:177.5                    3rd Qu.:129.78                    3rd Qu.:127.2                 
 Max.   :322.2                    Max.   :186.00                    Max.   :192.3                 
                                                                                                  
 n_rep_trabajo_max_extension_no_implicado n_rep_trabajo_max_extension_implicado
 Min.   :1.000                            Min.   : 1.00                        
 1st Qu.:2.000                            1st Qu.: 3.00                        
 Median :3.000                            Median : 4.00                        
 Mean   :3.425                            Mean   : 4.02                        
 3rd Qu.:4.000                            3rd Qu.: 5.00                        
 Max.   :8.000                            Max.   :13.00                        
                                                                               
 n_rep_trabajo_max_flexion_no_implicado n_rep_trabajo_max_flexion_implicado
 Min.   : 1.000                         Min.   : 1.000                     
 1st Qu.: 2.000                         1st Qu.: 2.000                     
 Median : 3.000                         Median : 3.000                     
 Mean   : 3.083                         Mean   : 3.242                     
 3rd Qu.: 4.000                         3rd Qu.: 4.000                     
 Max.   :14.000                         Max.   :15.000                     
                                                                           
 trabj_peso_corporal_extension_no_implicado trabj_peso_corporal_extension_implicado
 Min.   : 61.5                              Min.   : 50.2                          
 1st Qu.:193.6                              1st Qu.:168.0                          
 Median :232.7                              Median :196.1                          
 Mean   :237.7                              Mean   :204.0                          
 3rd Qu.:274.0                              3rd Qu.:238.0                          
 Max.   :417.7                              Max.   :374.1                          
                                                                                   
 trabj_peso_corporal_flexion_no_implicado trabj_peso_corporal_flexion_implicado
 Min.   : 27.5                            Min.   : 45.6                        
 1st Qu.:141.7                            1st Qu.:129.8                        
 Median :165.8                            Median :157.9                        
 Mean   :169.0                            Mean   :161.6                        
 3rd Qu.:198.1                            3rd Qu.:193.1                        
 Max.   :300.9                            Max.   :334.7                        
                                                                               
 trabajo_total_extension_no_implicado trabajo_total_extension_implicado
 Min.   : 713.9                       Min.   : 439.5                   
 1st Qu.:1156.0                       1st Qu.: 983.7                   
 Median :1551.8                       Median :1360.2                   
 Mean   :1502.4                       Mean   :1298.9                   
 3rd Qu.:1817.8                       3rd Qu.:1611.5                   
 Max.   :2397.6                       Max.   :2293.0                   
                                                                       
 trabajo_total_flexion_no_implicado trabajo_total_flexion_implicado rango_de_mov_extension_no_implicado
 Min.   : 275.2                     Min.   : 315.4                  Min.   :63.80                      
 1st Qu.: 769.3                     1st Qu.: 733.2                  1st Qu.:87.78                      
 Median :1079.6                     Median :1019.3                  Median :88.70                      
 Mean   :1069.5                     Mean   :1027.0                  Mean   :86.82                      
 3rd Qu.:1337.9                     3rd Qu.:1310.6                  3rd Qu.:89.22                      
 Max.   :1901.8                     Max.   :1844.9                  Max.   :91.40                      
                                                                                                       
 rango_de_mov_extension_implicado rango_de_mov_flexion_no_implicado rango_de_mov_flexion_implicado
 Min.   :54.40                    Min.   :63.80                     Min.   :54.40                 
 1st Qu.:86.67                    1st Qu.:87.78                     1st Qu.:86.67                 
 Median :88.30                    Median :88.70                     Median :88.30                 
 Mean   :86.27                    Mean   :86.82                     Mean   :86.27                 
 3rd Qu.:89.00                    3rd Qu.:89.22                     3rd Qu.:89.00                 
 Max.   :90.90                    Max.   :91.40                     Max.   :90.90                 
                                                                                                  
 razon_agon_antagon_extension_no_implicado razon_agon_antagon_extension_implicado
 Min.   : 29.40                            Min.   : 39.50                        
 1st Qu.: 56.25                            1st Qu.: 64.78                        
 Median : 63.90                            Median : 73.20                        
 Mean   : 64.87                            Mean   : 75.23                        
 3rd Qu.: 74.12                            3rd Qu.: 86.00                        
 Max.   :103.60                            Max.   :129.00                        
                                                                                 
 razon_agon_antagon_flexion_no_implicado razon_agon_antagon_flexion_implicado
 Min.   :-19.700                         Min.   :-41.500                     
 1st Qu.:  1.625                         1st Qu.:  3.175                     
 Median : 10.100                         Median :  9.800                     
 Mean   : 10.796                         Mean   : 11.258                     
 3rd Qu.: 20.300                         3rd Qu.: 20.375                     
 Max.   : 43.600                         Max.   : 48.900                     
 NA's   :96                              NA's   :96                          
 simetria_pico_del_par_extension_percent simetria_pico_del_par_flexion_percent
 Length:252                              Length:252                           
 Class :character                        Class :character                     
 Mode  :character                        Mode  :character                     
                                                                              
                                                                              
                                                                              
                                                                              
 simetria_trabj_peso_corporal_extension_percent simetria_trabj_peso_corporal_flexion_percent
 Length:252                                     Length:252                                  
 Class :character                               Class :character                            
 Mode  :character                               Mode  :character                            
                                                                                            
                                                                                            
                                                                                            
                                                                                            
 simetria_trabajo_total_extension_percent simetria_trabajo_total_flexion_percent edad_evaluacion
 Length:252                               Length:252                             Min.   :17.00  
 Class :character                         Class :character                       1st Qu.:21.00  
 Mode  :character                         Mode  :character                       Median :24.00  
                                                                                 Mean   :24.28  
                                                                                 3rd Qu.:26.00  
                                                                                 Max.   :36.00  
                                                                                 NA's   :9      
 momento_pop_meses_redondeado categoria_momento_pop_derivada simetria_pico_par_extension
 Min.   : 3.000               <6 meses    :75                Min.   : 44.16             
 1st Qu.: 5.750               6 a <8 meses:78                1st Qu.: 76.72             
 Median : 7.000               ≥8 meses    :99                Median : 86.16             
 Mean   : 7.881                                              Mean   : 85.46             
 3rd Qu.: 9.000                                              3rd Qu.: 94.28             
 Max.   :26.000                                              Max.   :123.22             
                                                                                        
 simetria_pico_par_flexion simetria_trabj_peso_corporal_extension simetria_trabj_peso_corporal_flexion
 Min.   : 64.86            Min.   : 54.09                         Min.   : 51.45                      
 1st Qu.: 88.20            1st Qu.: 77.54                         1st Qu.: 84.84                      
 Median : 98.15            Median : 86.53                         Median : 96.01                      
 Mean   : 98.65            Mean   : 86.70                         Mean   : 98.12                      
 3rd Qu.:107.90            3rd Qu.: 96.12                         3rd Qu.:106.76                      
 Max.   :161.47            Max.   :125.34                         Max.   :287.64                      
                                                                                                      
 simetria_trabajo_total_extension simetria_trabajo_total_flexion
 Min.   : 51.13                   Min.   : 54.90                
 1st Qu.: 77.33                   1st Qu.: 85.84                
 Median : 85.71                   Median : 95.99                
 Mean   : 86.43                   Mean   : 98.43                
 3rd Qu.: 95.54                   3rd Qu.:106.25                
 Max.   :144.09                   Max.   :341.42                
                                                                
skimr::skim(datos_iso)
── Data Summary ────────────────────────
                           Values   
Name                       datos_iso
Number of rows             252      
Number of columns          52       
_______________________             
Column type frequency:              
  character                7        
  Date                     2        
  factor                   8        
  numeric                  35       
________________________            
Group variables            None     

10. Faltantes y controles de calidad

Se revisó la cantidad de datos faltantes por variable y se realizaron controles de consistencia para identificar:

  • valores inconsistentes en categorías;
  • duplicados;
  • fechas inconsistentes;
  • edades improbables;
  • valores antropométricos atípicos;
  • valores negativos no esperados;
  • repeticiones con valores no enteros.
# ============================================================
# 7) TABLA GENERAL DE FALTANTES
# ============================================================

tabla_faltantes <- datos_iso %>%
  summarise(
    across(
      everything(),
      ~ sum(is.na(.))
    )
  ) %>%
  pivot_longer(
    cols = everything(),
    names_to = "variable",
    values_to = "n_faltantes"
  ) %>%
  mutate(
    porcentaje_faltantes = round(100 * n_faltantes / nrow(datos_iso), 1)
  ) %>%
  arrange(desc(n_faltantes))

tabla_faltantes

naniar::vis_miss(datos_iso)

visdat::vis_dat(datos_iso)



# ============================================================
# 8) RESUMEN DE VARIABLES CATEGÓRICAS
# ============================================================

vars_categoricas <- c(
  "implicado",
  "nivel_de_competencia",
  "categoria_momento_pop_original",
  "categoria_momento_pop_derivada",
  "injerto",
  "lemaire_refuerzo_lateral",
  "relesion",
  "velocidad"
)

resumen_categoricas <- purrr::map_dfr(
  vars_categoricas,
  function(v){
    
    datos_iso %>%
      group_by(valor = .data[[v]]) %>%
      summarise(n = n(), .groups = "drop") %>%
      mutate(
        variable = v,
        porcentaje = round(100 * n / nrow(datos_iso), 1)
      ) %>%
      select(variable, valor, n, porcentaje)
  }
)

resumen_categoricas


# ============================================================
# 9) VALORES NO ESPERADOS ANTES DE LA RECODIFICACIÓN
# ============================================================

valores_implicado_no_esperados <- datos_raw %>%
  transmute(
    implicado_original = implicado,
    implicado_normalizado = normalizar_texto(implicado)
  ) %>%
  filter(
    !is.na(implicado_normalizado),
    !implicado_normalizado %in% c("derecho", "izquierdo")
  ) %>%
  distinct()

valores_implicado_no_esperados


valores_injerto_no_esperados <- datos_raw %>%
  transmute(
    injerto_original = injerto,
    injerto_normalizado = normalizar_texto(injerto)
  ) %>%
  filter(
    !is.na(injerto_normalizado),
    !injerto_normalizado %in% c("hth", "stri", "cuadricipital")
  ) %>%
  distinct()

valores_injerto_no_esperados


valores_lemaire_no_esperados <- datos_raw %>%
  transmute(
    lemaire_original = lemaire_refuerzo_lateral,
    lemaire_normalizado = normalizar_texto(lemaire_refuerzo_lateral)
  ) %>%
  filter(
    !is.na(lemaire_normalizado),
    !lemaire_normalizado %in% c("si", "s", "yes", "y", "1", "no", "n", "0")
  ) %>%
  distinct()

valores_lemaire_no_esperados


valores_relesion_no_esperados <- datos_raw %>%
  transmute(
    relesion_original = relesion,
    relesion_normalizado = normalizar_texto(relesion)
  ) %>%
  filter(
    !is.na(relesion_normalizado),
    !relesion_normalizado %in% c("si", "s", "yes", "y", "1", "no", "n", "0")
  ) %>%
  distinct()

valores_relesion_no_esperados

11. Base a nivel de evaluación

La base original contiene una fila por:

  • jugador;
  • fecha de sesión;
  • velocidad de evaluación.

Para caracterizar la muestra y construir el flujo de selección, se generó una base con una fila por evaluación:

\[ \text{evaluación} = \text{ID del jugador} \times \text{fecha de sesión} \]

# ============================================================
# 10) BASE A NIVEL DE EVALUACIÓN
# ============================================================

evaluaciones_iso <- datos_iso %>%
  distinct(
    id,
    sesion,
    .keep_all = TRUE
  )

evaluaciones_iso

12. Flujo de selección de la cohorte analítica

Para el análisis descriptivo final se aplicaron los siguientes criterios:

  1. Excluir jugadores con al menos un registro de relesión = Sí.
  2. Excluir evaluaciones sin dato disponible de momento postoperatorio.
  3. Excluir evaluaciones realizadas a más de 18 meses de la cirugía.
  4. Excluir registros sin posibilidad de asignar grupo competitivo.

La exclusión por relesión se aplicó a nivel del jugador, ya que la relesión representa una característica clínica del caso y no de una fila aislada.

# ============================================================
# 11) FLUJO DE SELECCIÓN DE LA COHORTE ANALÍTICA
# ============================================================

# Jugadores con al menos una relesión registrada
ids_relesion_si <- datos_iso %>%
  group_by(id) %>%
  summarise(
    relesion_si = any(relesion == "Sí", na.rm = TRUE),
    .groups = "drop"
  ) %>%
  filter(relesion_si) %>%
  pull(id)


# Etapa 1: base inicial a nivel de evaluación
evaluaciones_iniciales <- evaluaciones_iso


# Etapa 2: excluir jugadores con relesión
evaluaciones_sin_relesion <- evaluaciones_iniciales %>%
  filter(!id %in% ids_relesion_si)


# Etapa 3: excluir evaluaciones sin momento POP
evaluaciones_con_pop <- evaluaciones_sin_relesion %>%
  filter(!is.na(momento_pop_meses))


# Etapa 4: excluir evaluaciones >18 meses
evaluaciones_hasta_18m <- evaluaciones_con_pop %>%
  filter(momento_pop_meses <= 18)


# Etapa 5: crear grupo competitivo y excluir no clasificables
evaluaciones_analisis <- evaluaciones_hasta_18m %>%
  mutate(
    grupo_competencia = case_when(
      nivel_de_competencia == "Primera división" ~ "Primera",
      nivel_de_competencia == "Segunda división" ~ "Segunda",
      nivel_de_competencia %in% c(
        "Tercera División",
        "Amateur"
      ) ~ "Tercera + Amateur",
      TRUE ~ NA_character_
    ),
    
    grupo_competencia = factor(
      grupo_competencia,
      levels = c(
        "Primera",
        "Segunda",
        "Tercera + Amateur"
      )
    )
  ) %>%
  filter(!is.na(grupo_competencia))


# Función auxiliar para resumir cada etapa
resumir_etapa_flujo <- function(data, etapa){
  
  tibble::tibble(
    etapa = etapa,
    n_jugadores = n_distinct(data$id),
    n_evaluaciones = nrow(data)
  )
}


flujo_exclusiones <- bind_rows(
  resumir_etapa_flujo(
    evaluaciones_iniciales,
    "Base inicial"
  ),
  resumir_etapa_flujo(
    evaluaciones_sin_relesion,
    "Luego de excluir jugadores con relesión"
  ),
  resumir_etapa_flujo(
    evaluaciones_con_pop,
    "Luego de excluir evaluaciones sin momento POP"
  ),
  resumir_etapa_flujo(
    evaluaciones_hasta_18m,
    "Luego de excluir evaluaciones >18 meses"
  ),
  resumir_etapa_flujo(
    evaluaciones_analisis,
    "Cohorte analítica final"
  )
) %>%
  mutate(
    jugadores_excluidos_desde_etapa_previa = lag(n_jugadores) - n_jugadores,
    evaluaciones_excluidas_desde_etapa_previa = lag(n_evaluaciones) - n_evaluaciones
  )

flujo_exclusiones


# Detalle de exclusiones por motivo
detalle_exclusiones <- bind_rows(
  
  evaluaciones_iniciales %>%
    filter(id %in% ids_relesion_si) %>%
    summarise(
      motivo = "Jugadores con relesión",
      n_jugadores_excluidos = n_distinct(id),
      n_evaluaciones_excluidas = n()
    ),
  
  evaluaciones_sin_relesion %>%
    filter(is.na(momento_pop_meses)) %>%
    summarise(
      motivo = "Evaluaciones sin momento POP disponible",
      n_jugadores_excluidos = n_distinct(id),
      n_evaluaciones_excluidas = n()
    ),
  
  evaluaciones_con_pop %>%
    filter(momento_pop_meses > 18) %>%
    summarise(
      motivo = "Evaluaciones realizadas a >18 meses",
      n_jugadores_excluidos = n_distinct(id),
      n_evaluaciones_excluidas = n()
    ),
  
  evaluaciones_hasta_18m %>%
    mutate(
      grupo_competencia = case_when(
        nivel_de_competencia == "Primera división" ~ "Primera",
        nivel_de_competencia == "Segunda división" ~ "Segunda",
        nivel_de_competencia %in% c(
          "Tercera División",
          "Amateur"
        ) ~ "Tercera + Amateur",
        TRUE ~ NA_character_
      )
    ) %>%
    filter(is.na(grupo_competencia)) %>%
    summarise(
      motivo = "Evaluaciones sin grupo competitivo clasificable",
      n_jugadores_excluidos = n_distinct(id),
      n_evaluaciones_excluidas = n()
    )
)

detalle_exclusiones

Interpretación del flujo

Se partió de 58 jugadores y 84 evaluaciones a nivel de sesión.

Durante la depuración:

  • se excluyeron 10 jugadores con relesión, correspondientes a 12 evaluaciones;
  • se excluyeron 0 evaluaciones sin momento postoperatorio disponible;
  • se excluyeron 2 evaluaciones realizadas a más de 18 meses de la cirugía;
  • se excluyeron 0 evaluaciones sin nivel competitivo clasificable.

La cohorte analítica final quedó conformada por 46 jugadores y 70 evaluaciones.

Esta información puede utilizarse directamente para construir el diagrama de flujo de selección de la muestra.


13. Reconstrucción de la base analítica con las tres velocidades

Una vez definida la cohorte de evaluaciones elegibles, se recuperan las tres filas correspondientes a las velocidades isocinéticas.

# ============================================================
# 12) BASE ANALÍTICA FINAL CON LAS TRES VELOCIDADES
# ============================================================

datos_analisis <- datos_iso %>%
  inner_join(
    evaluaciones_analisis %>%
      select(
        id,
        sesion,
        grupo_competencia
      ),
    by = c("id", "sesion")
  )

resumen_muestra_analitica <- tibble::tibble(
  n_jugadores = n_distinct(evaluaciones_analisis$id),
  n_evaluaciones = nrow(evaluaciones_analisis),
  n_filas_isocineticas = nrow(datos_analisis)
)

resumen_muestra_analitica

Interpretación de la muestra final

La cohorte analítica incluyó:

  • 46 jugadores;
  • 70 evaluaciones clínicas únicas;
  • 210 filas de datos isocinéticos, correspondientes a las distintas velocidades disponibles.

14. Distribución de jugadores y evaluaciones por nivel competitivo

# ============================================================
# 13) JUGADORES Y EVALUACIONES POR GRUPO COMPETITIVO
# ============================================================

conteo_muestra_por_grupo <- evaluaciones_analisis %>%
  group_by(grupo_competencia) %>%
  summarise(
    n_jugadores = n_distinct(id),
    n_evaluaciones = n(),
    .groups = "drop"
  )

conteo_muestra_por_grupo


texto_grupos <- conteo_muestra_por_grupo %>%
  mutate(
    texto = paste0(
      grupo_competencia,
      ": ",
      n_jugadores,
      " jugadores y ",
      n_evaluaciones,
      " evaluaciones"
    )
  ) %>%
  pull(texto) %>%
  paste(collapse = "; ")

texto_grupos
[1] "Primera: 18 jugadores y 29 evaluaciones; Segunda: 13 jugadores y 22 evaluaciones; Tercera + Amateur: 15 jugadores y 19 evaluaciones"

Interpretación

La distribución por nivel competitivo fue la siguiente:

Primera: 18 jugadores y 29 evaluaciones; Segunda: 13 jugadores y 22 evaluaciones; Tercera + Amateur: 15 jugadores y 19 evaluaciones.


15. Distribución de evaluaciones por grupo competitivo y momento postoperatorio

# ============================================================
# 14) JUGADORES Y EVALUACIONES POR GRUPO Y TIEMPO
# ============================================================

conteo_muestra_por_grupo_y_tiempo <- evaluaciones_analisis %>%
  group_by(
    grupo_competencia,
    categoria_momento_pop_derivada
  ) %>%
  summarise(
    n_jugadores = n_distinct(id),
    n_evaluaciones = n(),
    .groups = "drop"
  ) %>%
  arrange(
    grupo_competencia,
    categoria_momento_pop_derivada
  )

conteo_muestra_por_grupo_y_tiempo

16. Funciones de formato para las tablas descriptivas

# ============================================================
# 15) FUNCIONES DE FORMATO
# ============================================================

formatear_numero <- function(x, digitos = 2){
  
  ifelse(
    is.na(x),
    "NA",
    formatC(
      x,
      format = "f",
      digits = digitos,
      decimal.mark = ","
    )
  )
}


formatear_media_de <- function(media, de, digitos = 2){
  
  paste0(
    formatear_numero(media, digitos),
    " ± ",
    formatear_numero(de, digitos)
  )
}


formatear_mediana_iqr <- function(mediana, p25, p75, digitos = 2){
  
  paste0(
    formatear_numero(mediana, digitos),
    " [",
    formatear_numero(p25, digitos),
    "; ",
    formatear_numero(p75, digitos),
    "]"
  )
}


formatear_n_porcentaje <- function(n, porcentaje, digitos = 1){
  
  paste0(
    n,
    " (",
    formatear_numero(porcentaje, digitos),
    "%)"
  )
}

17. Tabla 1: caracterización general de la cohorte

La Tabla 1 se construyó a nivel de evaluación única. Las variables numéricas se resumen de forma amplia para permitir decidir posteriormente si la versión final del manuscrito reportará:

  • media y desvío estándar, o
  • mediana y P25–P75.

En esta etapa se presentan ambas formas de resumen.


17.1 Variables consideradas en la Tabla 1

# ============================================================
# 16) VARIABLES PARA TABLA 1
# ============================================================

vars_tabla1_numericas <- c(
  "edad_evaluacion",
  "altura",
  "peso",
  "momento_pop_meses"
)

etiquetas_tabla1_numericas <- c(
  "Edad al momento de la evaluación, años",
  "Altura, cm",
  "Peso, kg",
  "Momento postquirúrgico, meses"
)


vars_tabla1_categoricas <- c(
  "implicado",
  "categoria_momento_pop_derivada",
  "injerto",
  "lemaire_refuerzo_lateral"
)

etiquetas_tabla1_categoricas <- c(
  "Miembro implicado",
  "Momento de evaluación",
  "Tipo de injerto",
  "Refuerzo lateral tipo Lemaire"
)

17.2 Disponibilidad de datos de Tabla 1

# ============================================================
# 17) DISPONIBILIDAD DE DATOS DE TABLA 1
# ============================================================

n_disponibles_tabla1_total <- bind_rows(
  
  purrr::map2_dfr(
    vars_tabla1_numericas,
    etiquetas_tabla1_numericas,
    function(v, etiqueta){
      
      evaluaciones_analisis %>%
        filter(!is.na(.data[[v]])) %>%
        summarise(
          variable = etiqueta,
          n_evaluaciones_disponibles = n(),
          n_jugadores_disponibles = n_distinct(id)
        )
    }
  ),
  
  purrr::map2_dfr(
    vars_tabla1_categoricas,
    etiquetas_tabla1_categoricas,
    function(v, etiqueta){
      
      evaluaciones_analisis %>%
        filter(!is.na(.data[[v]])) %>%
        summarise(
          variable = etiqueta,
          n_evaluaciones_disponibles = n(),
          n_jugadores_disponibles = n_distinct(id)
        )
    }
  )
)

n_disponibles_tabla1_total


n_disponibles_tabla1_por_grupo <- bind_rows(
  
  purrr::map2_dfr(
    vars_tabla1_numericas,
    etiquetas_tabla1_numericas,
    function(v, etiqueta){
      
      evaluaciones_analisis %>%
        filter(!is.na(.data[[v]])) %>%
        group_by(grupo_competencia) %>%
        summarise(
          variable = etiqueta,
          n_evaluaciones_disponibles = n(),
          n_jugadores_disponibles = n_distinct(id),
          .groups = "drop"
        )
    }
  ),
  
  purrr::map2_dfr(
    vars_tabla1_categoricas,
    etiquetas_tabla1_categoricas,
    function(v, etiqueta){
      
      evaluaciones_analisis %>%
        filter(!is.na(.data[[v]])) %>%
        group_by(grupo_competencia) %>%
        summarise(
          variable = etiqueta,
          n_evaluaciones_disponibles = n(),
          n_jugadores_disponibles = n_distinct(id),
          .groups = "drop"
        )
    }
  )
) %>%
  arrange(
    variable,
    grupo_competencia
  )

n_disponibles_tabla1_por_grupo

Interpretación

La tabla de disponibilidad permite identificar sobre cuántas evaluaciones y jugadores se calculó cada variable.
Por ejemplo, si una variable tiene 70 evaluaciones disponibles y 46 jugadores disponibles, significa que esa información estuvo registrada en toda la cohorte final. Si presenta un número menor, existen datos faltantes para esa variable.


17.3 Tabla 1: variables numéricas

# ============================================================
# 18) TABLA 1 - VARIABLES NUMÉRICAS
# ============================================================

tabla1_numericas_resumen <- purrr::map2_dfr(
  vars_tabla1_numericas,
  etiquetas_tabla1_numericas,
  function(v, etiqueta){
    
    tmp <- evaluaciones_analisis %>%
      transmute(
        id,
        grupo_competencia,
        valor = .data[[v]]
      ) %>%
      filter(
        !is.na(valor),
        !is.na(grupo_competencia)
      )
    
    
    resumen_por_grupo <- tmp %>%
      group_by(grupo_competencia) %>%
      summarise(
        variable = etiqueta,
        n_evaluaciones_disponibles = n(),
        n_jugadores_disponibles = n_distinct(id),
        media = mean(valor, na.rm = TRUE),
        de = sd(valor, na.rm = TRUE),
        mediana = median(valor, na.rm = TRUE),
        p25 = quantile(valor, 0.25, na.rm = TRUE),
        p75 = quantile(valor, 0.75, na.rm = TRUE),
        iqr = IQR(valor, na.rm = TRUE),
        minimo = min(valor, na.rm = TRUE),
        maximo = max(valor, na.rm = TRUE),
        .groups = "drop"
      )
    
    
    resumen_total <- tmp %>%
      summarise(
        grupo_competencia = "Total",
        variable = etiqueta,
        n_evaluaciones_disponibles = n(),
        n_jugadores_disponibles = n_distinct(id),
        media = mean(valor, na.rm = TRUE),
        de = sd(valor, na.rm = TRUE),
        mediana = median(valor, na.rm = TRUE),
        p25 = quantile(valor, 0.25, na.rm = TRUE),
        p75 = quantile(valor, 0.75, na.rm = TRUE),
        iqr = IQR(valor, na.rm = TRUE),
        minimo = min(valor, na.rm = TRUE),
        maximo = max(valor, na.rm = TRUE)
      )
    
    
    bind_rows(
      resumen_por_grupo %>%
        mutate(grupo_competencia = as.character(grupo_competencia)),
      resumen_total
    )
  }
) %>%
  relocate(
    variable,
    grupo_competencia,
    n_evaluaciones_disponibles,
    n_jugadores_disponibles
  )

tabla1_numericas_resumen


tabla1_numericas_formateada <- tabla1_numericas_resumen %>%
  mutate(
    media_de = formatear_media_de(
      media,
      de,
      digitos = 2
    ),
    mediana_p25_p75 = formatear_mediana_iqr(
      mediana,
      p25,
      p75,
      digitos = 2
    )
  ) %>%
  select(
    variable,
    grupo_competencia,
    n_evaluaciones_disponibles,
    n_jugadores_disponibles,
    media_de,
    mediana_p25_p75,
    iqr,
    minimo,
    maximo
  )

tabla1_numericas_formateada

17.4 Tabla 1: variables categóricas

# ============================================================
# 19) TABLA 1 - VARIABLES CATEGÓRICAS
# ============================================================

tabla1_categoricas_resumen <- purrr::map2_dfr(
  vars_tabla1_categoricas,
  etiquetas_tabla1_categoricas,
  function(v, etiqueta){
    
    tmp <- evaluaciones_analisis %>%
      transmute(
        id,
        grupo_competencia,
        categoria = as.character(.data[[v]])
      ) %>%
      filter(
        !is.na(categoria),
        !is.na(grupo_competencia)
      )
    
    
    denominadores_grupo <- tmp %>%
      group_by(grupo_competencia) %>%
      summarise(
        n_evaluaciones_disponibles_variable = n(),
        n_jugadores_disponibles_variable = n_distinct(id),
        .groups = "drop"
      )
    
    
    resumen_por_grupo <- tmp %>%
      group_by(
        grupo_competencia,
        categoria
      ) %>%
      summarise(
        variable = etiqueta,
        n_evaluaciones_categoria = n(),
        n_jugadores_categoria = n_distinct(id),
        .groups = "drop"
      ) %>%
      left_join(
        denominadores_grupo,
        by = "grupo_competencia"
      ) %>%
      mutate(
        porcentaje_evaluaciones = 
          100 * n_evaluaciones_categoria /
          n_evaluaciones_disponibles_variable
      )
    
    
    denominador_total <- tmp %>%
      summarise(
        grupo_competencia = "Total",
        n_evaluaciones_disponibles_variable = n(),
        n_jugadores_disponibles_variable = n_distinct(id)
      )
    
    
    resumen_total <- tmp %>%
      group_by(categoria) %>%
      summarise(
        grupo_competencia = "Total",
        variable = etiqueta,
        n_evaluaciones_categoria = n(),
        n_jugadores_categoria = n_distinct(id),
        .groups = "drop"
      ) %>%
      bind_cols(
        denominador_total %>%
          select(
            n_evaluaciones_disponibles_variable,
            n_jugadores_disponibles_variable
          )
      ) %>%
      mutate(
        porcentaje_evaluaciones = 
          100 * n_evaluaciones_categoria /
          n_evaluaciones_disponibles_variable
      )
    
    
    bind_rows(
      resumen_por_grupo %>%
        mutate(grupo_competencia = as.character(grupo_competencia)),
      resumen_total
    )
  }
) %>%
  relocate(
    variable,
    categoria,
    grupo_competencia,
    n_evaluaciones_categoria,
    porcentaje_evaluaciones,
    n_jugadores_categoria,
    n_evaluaciones_disponibles_variable,
    n_jugadores_disponibles_variable
  )

tabla1_categoricas_resumen


tabla1_categoricas_formateada <- tabla1_categoricas_resumen %>%
  mutate(
    n_porcentaje_evaluaciones = formatear_n_porcentaje(
      n_evaluaciones_categoria,
      porcentaje_evaluaciones,
      digitos = 1
    )
  ) %>%
  select(
    variable,
    categoria,
    grupo_competencia,
    n_porcentaje_evaluaciones,
    n_jugadores_categoria,
    n_evaluaciones_disponibles_variable,
    n_jugadores_disponibles_variable
  )

tabla1_categoricas_formateada

18. Evaluación de la distribución de las variables numéricas de Tabla 1

La evaluación de distribución se realizó únicamente para las variables numéricas de la Tabla 1.
Este paso permite decidir, de cara a la versión final del manuscrito, si cada variable conviene reportarla como:

  • media (DE), o
  • mediana [P25–P75].

La decisión no debe basarse en un único indicador, sino en la combinación de:

  • forma de los histogramas;
  • gráficos Q-Q;
  • presencia de valores extremos;
  • asimetría;
  • test de Shapiro-Wilk.
# ============================================================
# 20) ANÁLISIS DE DISTRIBUCIÓN - TABLA 1
# ============================================================

asimetria_pearson <- function(x){
  
  x <- x[!is.na(x)]
  
  if(length(x) < 3 || sd(x) == 0){
    return(NA_real_)
  }
  
  3 * (mean(x) - median(x)) / sd(x)
}


contar_outliers_iqr <- function(x){
  
  x <- x[!is.na(x)]
  
  if(length(x) < 4){
    return(NA_integer_)
  }
  
  q1 <- quantile(x, 0.25, na.rm = TRUE)
  q3 <- quantile(x, 0.75, na.rm = TRUE)
  iqr <- q3 - q1
  
  limite_inferior <- q1 - 1.5 * iqr
  limite_superior <- q3 + 1.5 * iqr
  
  sum(
    x < limite_inferior |
      x > limite_superior,
    na.rm = TRUE
  )
}


shapiro_p_seguro <- function(x){
  
  x <- x[!is.na(x)]
  
  if(
    length(x) < 3 ||
    length(x) > 5000 ||
    length(unique(x)) < 3
  ){
    return(NA_real_)
  }
  
  tryCatch(
    shapiro.test(x)$p.value,
    error = function(e) NA_real_
  )
}


diagnostico_distribucion_tabla1_total <- purrr::map2_dfr(
  vars_tabla1_numericas,
  etiquetas_tabla1_numericas,
  function(v, etiqueta){
    
    x <- evaluaciones_analisis %>%
      pull(.data[[v]]) %>%
      na.omit()
    
    tibble::tibble(
      variable = etiqueta,
      n_evaluaciones = length(x),
      media = mean(x, na.rm = TRUE),
      de = sd(x, na.rm = TRUE),
      mediana = median(x, na.rm = TRUE),
      p25 = quantile(x, 0.25, na.rm = TRUE),
      p75 = quantile(x, 0.75, na.rm = TRUE),
      iqr = IQR(x, na.rm = TRUE),
      minimo = min(x, na.rm = TRUE),
      maximo = max(x, na.rm = TRUE),
      asimetria_pearson = asimetria_pearson(x),
      n_outliers_iqr = contar_outliers_iqr(x),
      shapiro_p = shapiro_p_seguro(x)
    )
  }
) %>%
  mutate(
    shapiro_p_formateado = case_when(
      is.na(shapiro_p) ~ NA_character_,
      shapiro_p < 0.001 ~ "<0,001",
      TRUE ~ formatC(
        shapiro_p,
        format = "f",
        digits = 3,
        decimal.mark = ","
      )
    ),
    
    interpretacion_shapiro = case_when(
      is.na(shapiro_p) ~ "No evaluable",
      shapiro_p < 0.05 ~ "Evidencia contra normalidad",
      TRUE ~ "Sin evidencia clara contra normalidad"
    ),
    
    interpretacion_asimetria = case_when(
      is.na(asimetria_pearson) ~ "No evaluable",
      abs(asimetria_pearson) < 0.5 ~ "Asimetría baja",
      abs(asimetria_pearson) >= 0.5 &
        abs(asimetria_pearson) < 1 ~ "Asimetría moderada",
      abs(asimetria_pearson) >= 1 ~ "Asimetría marcada"
    )
  )

diagnostico_distribucion_tabla1_total


diagnostico_distribucion_tabla1_por_grupo <- evaluaciones_analisis %>%
  select(
    id,
    grupo_competencia,
    all_of(vars_tabla1_numericas)
  ) %>%
  pivot_longer(
    cols = all_of(vars_tabla1_numericas),
    names_to = "variable_original",
    values_to = "valor"
  ) %>%
  filter(!is.na(valor)) %>%
  mutate(
    variable = case_when(
      variable_original == "edad_evaluacion" ~ 
        "Edad al momento de la evaluación, años",
      variable_original == "altura" ~ 
        "Altura, cm",
      variable_original == "peso" ~ 
        "Peso, kg",
      variable_original == "momento_pop_meses" ~ 
        "Momento postquirúrgico, meses",
      TRUE ~ variable_original
    )
  ) %>%
  group_by(
    variable,
    grupo_competencia
  ) %>%
  summarise(
    n_evaluaciones = n(),
    n_jugadores = n_distinct(id),
    media = mean(valor, na.rm = TRUE),
    de = sd(valor, na.rm = TRUE),
    mediana = median(valor, na.rm = TRUE),
    p25 = quantile(valor, 0.25, na.rm = TRUE),
    p75 = quantile(valor, 0.75, na.rm = TRUE),
    iqr = IQR(valor, na.rm = TRUE),
    minimo = min(valor, na.rm = TRUE),
    maximo = max(valor, na.rm = TRUE),
    asimetria_pearson = asimetria_pearson(valor),
    n_outliers_iqr = contar_outliers_iqr(valor),
    shapiro_p = shapiro_p_seguro(valor),
    .groups = "drop"
  ) %>%
  mutate(
    shapiro_p_formateado = case_when(
      is.na(shapiro_p) ~ NA_character_,
      shapiro_p < 0.001 ~ "<0,001",
      TRUE ~ formatC(
        shapiro_p,
        format = "f",
        digits = 3,
        decimal.mark = ","
      )
    ),
    
    interpretacion_shapiro = case_when(
      is.na(shapiro_p) ~ "No evaluable",
      shapiro_p < 0.05 ~ "Evidencia contra normalidad",
      TRUE ~ "Sin evidencia clara contra normalidad"
    ),
    
    interpretacion_asimetria = case_when(
      is.na(asimetria_pearson) ~ "No evaluable",
      abs(asimetria_pearson) < 0.5 ~ "Asimetría baja",
      abs(asimetria_pearson) >= 0.5 &
        abs(asimetria_pearson) < 1 ~ "Asimetría moderada",
      abs(asimetria_pearson) >= 1 ~ "Asimetría marcada"
    )
  )

diagnostico_distribucion_tabla1_por_grupo

18.1 Histogramas y gráficos Q-Q para Tabla 1

datos_tabla1_largo <- evaluaciones_analisis %>%
  select(
    grupo_competencia,
    all_of(vars_tabla1_numericas)
  ) %>%
  pivot_longer(
    cols = all_of(vars_tabla1_numericas),
    names_to = "variable_original",
    values_to = "valor"
  ) %>%
  filter(!is.na(valor)) %>%
  mutate(
    variable = case_when(
      variable_original == "edad_evaluacion" ~ 
        "Edad al momento de la evaluación, años",
      variable_original == "altura" ~ 
        "Altura, cm",
      variable_original == "peso" ~ 
        "Peso, kg",
      variable_original == "momento_pop_meses" ~ 
        "Momento postquirúrgico, meses",
      TRUE ~ variable_original
    )
  )


graficos_histogramas_tabla1 <- datos_tabla1_largo %>%
  split(.$variable) %>%
  purrr::map(
    ~ ggplot(
      .x,
      aes(x = valor)
    ) +
      geom_histogram(
        bins = 10,
        color = "black",
        fill = "grey80"
      ) +
      facet_wrap(
        ~ grupo_competencia,
        scales = "free_y"
      ) +
      labs(
        title = unique(.x$variable),
        x = "Valor",
        y = "Frecuencia"
      ) +
      theme_minimal()
  )


graficos_qq_tabla1 <- datos_tabla1_largo %>%
  split(.$variable) %>%
  purrr::map(
    ~ ggplot(
      .x,
      aes(sample = valor)
    ) +
      stat_qq() +
      stat_qq_line() +
      facet_wrap(
        ~ grupo_competencia,
        scales = "free"
      ) +
      labs(
        title = paste0(
          "Q-Q plot: ",
          unique(.x$variable)
        ),
        x = "Cuantiles teóricos",
        y = "Cuantiles observados"
      ) +
      theme_minimal()
  )


graficos_histogramas_tabla1[["Edad al momento de la evaluación, años"]]

graficos_histogramas_tabla1[["Altura, cm"]]

graficos_histogramas_tabla1[["Peso, kg"]]

graficos_histogramas_tabla1[["Momento postquirúrgico, meses"]]


graficos_qq_tabla1[["Edad al momento de la evaluación, años"]]

graficos_qq_tabla1[["Altura, cm"]]

graficos_qq_tabla1[["Peso, kg"]]

graficos_qq_tabla1[["Momento postquirúrgico, meses"]]


19. Tabla 2: descripción de variables de fuerza isocinética

La Tabla 2 resume las métricas de fuerza y desempeño isocinético según:

  • grupo competitivo;
  • velocidad de evaluación.

Se informan simultáneamente:

  • media y desvío estándar;
  • mediana y P25–P75;
  • IQR;
  • mínimo;
  • máximo;
  • cantidad de evaluaciones disponibles;
  • cantidad de jugadores que aportaron datos.

Esta estrategia evita tener que definir, para cada una de las múltiples variables de fuerza, un único estadístico de tendencia central.

# ============================================================
# 21) TABLA 2 - VARIABLES DE FUERZA ISOCINÉTICA
# ============================================================

vars_fuerza <- unique(c(
  vars_metricas_continuas,
  vars_repeticiones,
  vars_simetrias
))

vars_fuerza <- vars_fuerza[
  vars_fuerza %in% names(datos_analisis)
]


vars_fuerza_no_numericas <- vars_fuerza[
  !vapply(
    datos_analisis[vars_fuerza],
    is.numeric,
    logical(1)
  )
]

if(length(vars_fuerza_no_numericas) > 0){
  stop(
    paste0(
      "Hay variables de fuerza no numéricas: ",
      paste(vars_fuerza_no_numericas, collapse = ", ")
    )
  )
}


datos_fuerza_largo <- datos_analisis %>%
  select(
    id,
    sesion,
    velocidad,
    grupo_competencia,
    all_of(vars_fuerza)
  ) %>%
  pivot_longer(
    cols = all_of(vars_fuerza),
    names_to = "variable",
    values_to = "valor"
  ) %>%
  filter(
    !is.na(valor),
    !is.na(grupo_competencia),
    !is.na(velocidad)
  )


tabla2_fuerza_resumen_numerico <- datos_fuerza_largo %>%
  group_by(
    variable,
    velocidad,
    grupo_competencia
  ) %>%
  summarise(
    
    n_evaluaciones_disponibles = n_distinct(
      paste(id, sesion)
    ),
    
    n_jugadores_disponibles = n_distinct(id),
    
    media = mean(valor, na.rm = TRUE),
    de = sd(valor, na.rm = TRUE),
    
    mediana = median(valor, na.rm = TRUE),
    p25 = quantile(valor, 0.25, na.rm = TRUE),
    p75 = quantile(valor, 0.75, na.rm = TRUE),
    iqr = IQR(valor, na.rm = TRUE),
    
    minimo = min(valor, na.rm = TRUE),
    maximo = max(valor, na.rm = TRUE),
    
    .groups = "drop"
  )


tabla2_fuerza_formateada <- tabla2_fuerza_resumen_numerico %>%
  mutate(
    media_de = formatear_media_de(
      media,
      de,
      digitos = 2
    ),
    mediana_p25_p75 = formatear_mediana_iqr(
      mediana,
      p25,
      p75,
      digitos = 2
    )
  ) %>%
  select(
    variable,
    velocidad,
    grupo_competencia,
    n_evaluaciones_disponibles,
    n_jugadores_disponibles,
    media_de,
    mediana_p25_p75,
    iqr,
    minimo,
    maximo
  )

tabla2_fuerza_formateada

20. Exportación de Tabla 1 y Tabla 2 a Word

Se genera un archivo Word con:

  • Tabla 1, dividida en:
    • Panel A: variables numéricas;
    • Panel B: variables categóricas.
  • Tabla 2 de variables de fuerza.

Se agrega un epígrafe explicativo sobre la cantidad de evaluaciones calculada.


archivo_word_tablas <- file.path(
  getwd(),
  "Tablas_1_y_2_descriptivas_isocinesia_RLCA.docx"
)


doc <- read_docx()

doc <- body_add_par(
  doc,
  "Tablas descriptivas del análisis de fuerza isocinética en futbolistas con RLCA",
  style = "heading 1"
)

doc <- body_add_par(
  doc,
  paste0(
    "Cohorte analítica final: ",
    resumen_muestra_analitica$n_jugadores,
    " jugadores, ",
    resumen_muestra_analitica$n_evaluaciones,
    " evaluaciones clínicas únicas y ",
    resumen_muestra_analitica$n_filas_isocineticas,
    " registros isocinéticos por velocidad."
  ),
  style = "Normal"
)

doc <- body_add_par(
  doc,
  "Tabla 1. Características generales de la cohorte analítica",
  style = "heading 1"
)

doc <- body_add_par(
  doc,
  "Panel A. Variables numéricas.",
  style = "heading 2"
)

doc <- body_add_flextable(
  doc,
  value = ft_tabla1_num
)

doc <- body_add_par(
  doc,
  "* Evaluaciones disponibles corresponde a combinaciones únicas de jugador y fecha de sesión (ID × sesión). Un mismo jugador pudo aportar más de una evaluación si fue medido en distintos momentos postoperatorios.",
  style = "Normal"
)

doc <- body_add_par(
  doc,
  "Panel B. Variables categóricas.",
  style = "heading 2"
)

doc <- body_add_flextable(
  doc,
  value = ft_tabla1_cat
)

doc <- body_add_par(
  doc,
  "* Los porcentajes se calcularon sobre las evaluaciones con dato disponible para cada variable, excluyendo los faltantes.",
  style = "Normal"
)

doc <- body_add_par(
  doc,
  "Tabla 2. Descripción de las variables de fuerza isocinética según velocidad y nivel competitivo",
  style = "heading 1"
)

doc <- body_add_flextable(
  doc,
  value = ft_tabla2
)

doc <- body_add_par(
  doc,
  "* Evaluaciones disponibles corresponde al número de evaluaciones clínicas únicas utilizadas para calcular cada variable dentro de cada velocidad y grupo competitivo. Un mismo jugador pudo aportar más de una evaluación en diferentes momentos postoperatorios.",
  style = "Normal"
)

print(
  doc,
  target = archivo_word_tablas
)

archivo_word_tablas
[1] "C:/Users/Juan_Cruz/Desktop/Cosas Juan kinesiologia/Fuerza isocinetica futbol argentino/Isocinesia/Tablas_1_y_2_descriptivas_isocinesia_RLCA.docx"

21. Figuras descriptivas propuestas

Justificación del formato gráfico

Para representar variables continuas se propone utilizar puntos con barras verticales P25–P75, en lugar de barras macizas. Esta elección permite mostrar simultáneamente:

  • la tendencia central;
  • la dispersión intercuartílica;
  • la comparación visual entre grupos y tiempos.

Se diseñan tres figuras:

  1. Pico de par en extensión, con paneles para miembro implicado y no implicado.
  2. Pico de par en flexión, con paneles para miembro implicado y no implicado.
  3. Simetría del pico de par, con paneles para extensión y flexión.

Cada figura se estratifica por:

  • velocidad de evaluación;
  • momento postoperatorio;
  • grupo competitivo.

21.1 Función auxiliar para gráficos de mediana e IQR

# ============================================================
# 23) FUNCIÓN AUXILIAR PARA FIGURAS DESCRIPTIVAS
# ============================================================

resumir_para_figura <- function(data, variable_valor){
  
  data %>%
    filter(!is.na(.data[[variable_valor]])) %>%
    group_by(
      velocidad,
      grupo_competencia,
      categoria_momento_pop_derivada
    ) %>%
    summarise(
      mediana = median(.data[[variable_valor]], na.rm = TRUE),
      p25 = quantile(.data[[variable_valor]], 0.25, na.rm = TRUE),
      p75 = quantile(.data[[variable_valor]], 0.75, na.rm = TRUE),
      n_evaluaciones = n_distinct(paste(id, sesion)),
      n_jugadores = n_distinct(id),
      .groups = "drop"
    )
}

21.2 Figura 1. Pico de par en extensión: implicado y no implicado

# ============================================================
# 24) FIGURA 1 - PICO DE PAR EN EXTENSIÓN
# ============================================================

datos_figura_extension <- datos_analisis %>%
  select(
    id,
    sesion,
    velocidad,
    grupo_competencia,
    categoria_momento_pop_derivada,
    pico_del_par_extension_implicado,
    pico_del_par_extension_no_implicado
  ) %>%
  pivot_longer(
    cols = c(
      pico_del_par_extension_implicado,
      pico_del_par_extension_no_implicado
    ),
    names_to = "miembro",
    values_to = "valor"
  ) %>%
  mutate(
    miembro = case_when(
      miembro == "pico_del_par_extension_implicado" ~ "Implicado",
      miembro == "pico_del_par_extension_no_implicado" ~ "No implicado",
      TRUE ~ miembro
    ),
    miembro = factor(
      miembro,
      levels = c("No implicado", "Implicado")
    ),
    velocidad_etiqueta = paste0(as.character(velocidad), "°/s")
  ) %>%
  filter(!is.na(valor)) %>%
  group_by(
    miembro,
    velocidad_etiqueta,
    grupo_competencia,
    categoria_momento_pop_derivada
  ) %>%
  summarise(
    mediana = median(valor, na.rm = TRUE),
    p25 = quantile(valor, 0.25, na.rm = TRUE),
    p75 = quantile(valor, 0.75, na.rm = TRUE),
    .groups = "drop"
  )


figura_extension <- ggplot(
  datos_figura_extension,
  aes(
    x = categoria_momento_pop_derivada,
    y = mediana,
    color = grupo_competencia
  )
) +
  geom_errorbar(
    aes(
      ymin = p25,
      ymax = p75
    ),
    width = 0.18,
    position = position_dodge(width = 0.55),
    linewidth = 0.7
  ) +
  geom_point(
    position = position_dodge(width = 0.55),
    size = 2.7
  ) +
  facet_grid(
    miembro ~ velocidad_etiqueta
  ) +
  labs(
    title = "Pico de par en extensión según grupo competitivo y momento postoperatorio",
    subtitle = "Puntos: mediana. Barras verticales: P25–P75.",
    x = "Momento postoperatorio",
    y = "Pico de par en extensión",
    color = "Grupo competitivo"
  ) +
  theme_minimal(base_size = 11) +
  theme(
    legend.position = "bottom",
    panel.grid.minor = element_blank(),
    strip.text = element_text(face = "bold")
  )

figura_extension


21.3 Figura 2. Pico de par en flexión: implicado y no implicado

# ============================================================
# 25) FIGURA 2 - PICO DE PAR EN FLEXIÓN
# ============================================================

datos_figura_flexion <- datos_analisis %>%
  select(
    id,
    sesion,
    velocidad,
    grupo_competencia,
    categoria_momento_pop_derivada,
    pico_del_par_flexion_implicado,
    pico_del_par_flexion_no_implicado
  ) %>%
  pivot_longer(
    cols = c(
      pico_del_par_flexion_implicado,
      pico_del_par_flexion_no_implicado
    ),
    names_to = "miembro",
    values_to = "valor"
  ) %>%
  mutate(
    miembro = case_when(
      miembro == "pico_del_par_flexion_implicado" ~ "Implicado",
      miembro == "pico_del_par_flexion_no_implicado" ~ "No implicado",
      TRUE ~ miembro
    ),
    miembro = factor(
      miembro,
      levels = c("No implicado", "Implicado")
    ),
    velocidad_etiqueta = paste0(as.character(velocidad), "°/s")
  ) %>%
  filter(!is.na(valor)) %>%
  group_by(
    miembro,
    velocidad_etiqueta,
    grupo_competencia,
    categoria_momento_pop_derivada
  ) %>%
  summarise(
    mediana = median(valor, na.rm = TRUE),
    p25 = quantile(valor, 0.25, na.rm = TRUE),
    p75 = quantile(valor, 0.75, na.rm = TRUE),
    .groups = "drop"
  )


figura_flexion <- ggplot(
  datos_figura_flexion,
  aes(
    x = categoria_momento_pop_derivada,
    y = mediana,
    color = grupo_competencia
  )
) +
  geom_errorbar(
    aes(
      ymin = p25,
      ymax = p75
    ),
    width = 0.18,
    position = position_dodge(width = 0.55),
    linewidth = 0.7
  ) +
  geom_point(
    position = position_dodge(width = 0.55),
    size = 2.7
  ) +
  facet_grid(
    miembro ~ velocidad_etiqueta
  ) +
  labs(
    title = "Pico de par en flexión según grupo competitivo y momento postoperatorio",
    subtitle = "Puntos: mediana. Barras verticales: P25–P75.",
    x = "Momento postoperatorio",
    y = "Pico de par en flexión",
    color = "Grupo competitivo"
  ) +
  theme_minimal(base_size = 11) +
  theme(
    legend.position = "bottom",
    panel.grid.minor = element_blank(),
    strip.text = element_text(face = "bold")
  )

figura_flexion


21.4 Figura 3. Simetría del pico de par en extensión y flexión

# ============================================================
# 26) FIGURA 3 - SIMETRÍA DEL PICO DE PAR
# ============================================================

datos_figura_simetria <- datos_analisis %>%
  select(
    id,
    sesion,
    velocidad,
    grupo_competencia,
    categoria_momento_pop_derivada,
    simetria_pico_par_extension,
    simetria_pico_par_flexion
  ) %>%
  pivot_longer(
    cols = c(
      simetria_pico_par_extension,
      simetria_pico_par_flexion
    ),
    names_to = "movimiento",
    values_to = "valor"
  ) %>%
  mutate(
    movimiento = case_when(
      movimiento == "simetria_pico_par_extension" ~ "Extensión",
      movimiento == "simetria_pico_par_flexion" ~ "Flexión",
      TRUE ~ movimiento
    ),
    movimiento = factor(
      movimiento,
      levels = c("Extensión", "Flexión")
    ),
    velocidad_etiqueta = paste0(as.character(velocidad), "°/s")
  ) %>%
  filter(!is.na(valor)) %>%
  group_by(
    movimiento,
    velocidad_etiqueta,
    grupo_competencia,
    categoria_momento_pop_derivada
  ) %>%
  summarise(
    mediana = median(valor, na.rm = TRUE),
    p25 = quantile(valor, 0.25, na.rm = TRUE),
    p75 = quantile(valor, 0.75, na.rm = TRUE),
    .groups = "drop"
  )


figura_simetria <- ggplot(
  datos_figura_simetria,
  aes(
    x = categoria_momento_pop_derivada,
    y = mediana,
    color = grupo_competencia
  )
) +
  geom_hline(
    yintercept = 100,
    linetype = "dashed",
    color = "grey50"
  ) +
  geom_errorbar(
    aes(
      ymin = p25,
      ymax = p75
    ),
    width = 0.18,
    position = position_dodge(width = 0.55),
    linewidth = 0.7
  ) +
  geom_point(
    position = position_dodge(width = 0.55),
    size = 2.7
  ) +
  facet_grid(
    movimiento ~ velocidad_etiqueta
  ) +
  labs(
    title = "Simetría del pico de par según grupo competitivo y momento postoperatorio",
    subtitle = "Puntos: mediana. Barras verticales: P25–P75. Línea discontinua: 100% de simetría.",
    x = "Momento postoperatorio",
    y = "Simetría del pico de par (%)",
    color = "Grupo competitivo"
  ) +
  theme_minimal(base_size = 11) +
  theme(
    legend.position = "bottom",
    panel.grid.minor = element_blank(),
    strip.text = element_text(face = "bold")
  )

figura_simetria


22. Guardado de figuras

# ============================================================
# 27) GUARDAR FIGURAS
# ============================================================

carpeta_figuras <- file.path(
  getwd(),
  "figuras_isocinesia_RLCA"
)

dir.create(
  carpeta_figuras,
  showWarnings = FALSE
)

ggsave(
  filename = file.path(
    carpeta_figuras,
    "Figura_1_pico_par_extension.png"
  ),
  plot = figura_extension,
  width = 13,
  height = 7.5,
  dpi = 300
)

ggsave(
  filename = file.path(
    carpeta_figuras,
    "Figura_2_pico_par_flexion.png"
  ),
  plot = figura_flexion,
  width = 13,
  height = 7.5,
  dpi = 300
)

ggsave(
  filename = file.path(
    carpeta_figuras,
    "Figura_3_simetria_pico_par.png"
  ),
  plot = figura_simetria,
  width = 13,
  height = 7.5,
  dpi = 300
)

carpeta_figuras
[1] "C:/Users/Juan_Cruz/Desktop/Cosas Juan kinesiologia/Fuerza isocinetica futbol argentino/Isocinesia/figuras_isocinesia_RLCA"

23. Síntesis final del análisis descriptivo

El análisis permitió construir una cohorte analítica depurada de 46 jugadores y 70 evaluaciones clínicas únicas, excluyendo jugadores con relesión, evaluaciones sin momento postoperatorio y evaluaciones realizadas más allá de los 18 meses posteriores a la cirugía.

La Tabla 1 resume las características generales de la cohorte según nivel competitivo. Las variables numéricas se presentan en formato amplio, incluyendo media, desvío estándar, mediana, P25–P75, IQR, mínimo y máximo, con el objetivo de definir posteriormente el formato de reporte más adecuado para la versión final del manuscrito. Las variables categóricas se presentan mediante frecuencias absolutas y relativas calculadas sobre los datos disponibles.

La Tabla 2 describe los resultados de fuerza isocinética según grupo competitivo y velocidad de evaluación. Para todas las variables de fuerza se informan medidas de tendencia central y dispersión tanto paramétricas como robustas, evitando imponer una única forma de resumen sobre un conjunto amplio y heterogéneo de métricas.

Las figuras descriptivas propuestas permiten visualizar el comportamiento del pico de par en extensión, del pico de par en flexión y de las simetrías correspondientes según nivel competitivo y momento postoperatorio. Se eligió una representación basada en medianas y rangos intercuartílicos para ofrecer una lectura más estable frente a posibles valores extremos.

# ============================================================
# 28) FIGURAS FINALES TIPO BOXPLOT + PUNTOS
# ============================================================
# 4 figuras principales:
# 1) Pico del par en extensión - implicado
# 2) Pico del par en flexión - implicado
# 3) Simetría del pico del par en extensión
# 4) Simetría del pico del par en flexión
#
# Cada figura se divide en 3 subpaneles:
# a) 60°/s
# b) 180°/s
# c) 300°/s
#
# Eje X: momento postoperatorio
# Eje Y: variable numérica
# Color/relleno: grupo competitivo
# Puntos: evaluaciones individuales
# ============================================================


# ============================================================
# 28.1) Crear carpeta para guardar las figuras finales
# ============================================================

carpeta_figuras_boxplot <- file.path(
  getwd(),
  "figuras_isocinesia_RLCA",
  "boxplots_finales"
)

dir.create(
  carpeta_figuras_boxplot,
  recursive = TRUE,
  showWarnings = FALSE
)


# ============================================================
# 28.2) Función para preparar datos
# ============================================================

preparar_datos_boxplot <- function(
    data,
    variable_valor
){
  
  data %>%
    select(
      id,
      sesion,
      velocidad,
      grupo_competencia,
      categoria_momento_pop_derivada,
      all_of(variable_valor)
    ) %>%
    rename(
      valor = all_of(variable_valor)
    ) %>%
    filter(
      !is.na(valor),
      !is.na(velocidad),
      !is.na(grupo_competencia),
      !is.na(categoria_momento_pop_derivada)
    ) %>%
    mutate(
      velocidad_num = readr::parse_number(as.character(velocidad)),
      
      velocidad_etiqueta = case_when(
        velocidad_num == 60 ~ "a) 60°/s",
        velocidad_num == 180 ~ "b) 180°/s",
        velocidad_num == 300 ~ "c) 300°/s",
        TRUE ~ NA_character_
      ),
      
      velocidad_etiqueta = factor(
        velocidad_etiqueta,
        levels = c("a) 60°/s", "b) 180°/s", "c) 300°/s")
      ),
      
      categoria_momento_pop_derivada = factor(
        categoria_momento_pop_derivada,
        levels = c("<6 meses", "6 a <8 meses", "≥8 meses")
      ),
      
      grupo_competencia = factor(
        grupo_competencia,
        levels = c("Primera", "Segunda", "Tercera + Amateur")
      )
    ) %>%
    filter(!is.na(velocidad_etiqueta))
}


# ============================================================
# 28.3) Función para tabla de n por celda
# ============================================================

crear_tabla_n_figura <- function(
    data_plot,
    nombre_figura
){
  
  data_plot %>%
    group_by(
      velocidad_etiqueta,
      categoria_momento_pop_derivada,
      grupo_competencia
    ) %>%
    summarise(
      n_evaluaciones = n_distinct(paste(id, sesion)),
      n_jugadores = n_distinct(id),
      .groups = "drop"
    ) %>%
    mutate(
      figura = nombre_figura,
      bajo_n = case_when(
        n_evaluaciones < 5 ~ "Sí",
        TRUE ~ "No"
      )
    ) %>%
    relocate(
      figura,
      velocidad_etiqueta,
      categoria_momento_pop_derivada,
      grupo_competencia
    )
}


# ============================================================
# 28.4) Función para posición de etiquetas n
# ============================================================

crear_posicion_n <- function(data_plot){
  
  rango_por_velocidad <- data_plot %>%
    group_by(velocidad_etiqueta) %>%
    summarise(
      y_min = min(valor, na.rm = TRUE),
      y_max = max(valor, na.rm = TRUE),
      rango = y_max - y_min,
      .groups = "drop"
    ) %>%
    mutate(
      y_n = y_max + 0.08 * rango
    )
  
  data_plot %>%
    group_by(
      velocidad_etiqueta,
      categoria_momento_pop_derivada,
      grupo_competencia
    ) %>%
    summarise(
      n_evaluaciones = n_distinct(paste(id, sesion)),
      n_jugadores = n_distinct(id),
      .groups = "drop"
    ) %>%
    left_join(
      rango_por_velocidad,
      by = "velocidad_etiqueta"
    ) %>%
    mutate(
      etiqueta_n = paste0("n=", n_evaluaciones)
    )
}


# ============================================================
# 28.5) Función para crear boxplot final
# ============================================================

crear_boxplot_final <- function(
    data_plot,
    titulo,
    subtitulo,
    etiqueta_y,
    agregar_linea_100 = FALSE,
    mostrar_n = FALSE
){
  
  datos_n <- crear_posicion_n(data_plot)
  
  grafico <- ggplot(
    data_plot,
    aes(
      x = categoria_momento_pop_derivada,
      y = valor,
      fill = grupo_competencia,
      color = grupo_competencia
    )
  )
  
  if(agregar_linea_100){
    grafico <- grafico +
      geom_hline(
        yintercept = 100,
        linetype = "dashed",
        color = "grey35",
        linewidth = 0.5
      )
  }
  
  grafico <- grafico +
    geom_boxplot(
      width = 0.58,
      alpha = 0.30,
      outlier.shape = NA,
      linewidth = 0.50,
      position = position_dodge(width = 0.75)
    ) +
    geom_point(
      position = position_jitterdodge(
        jitter.width = 0.10,
        jitter.height = 0,
        dodge.width = 0.75,
        seed = 123
      ),
      size = 1.7,
      alpha = 0.65,
      stroke = 0.20
    )
  
  if(mostrar_n){
    
    grafico <- grafico +
      geom_text(
        data = datos_n,
        aes(
          x = categoria_momento_pop_derivada,
          y = y_n,
          label = etiqueta_n,
          color = grupo_competencia,
          group = grupo_competencia
        ),
        position = position_dodge(width = 0.75),
        inherit.aes = FALSE,
        size = 3.0,
        show.legend = FALSE
      )
  }
  
  grafico +
    facet_wrap(
      ~ velocidad_etiqueta,
      nrow = 1
    ) +
    scale_y_continuous(
      expand = expansion(mult = c(0.04, 0.15))
    ) +
    labs(
      title = titulo,
      subtitle = subtitulo,
      x = "Momento postoperatorio",
      y = etiqueta_y,
      fill = "Grupo competitivo",
      color = "Grupo competitivo",
      caption = "Cada punto representa una evaluación clínica única. Un mismo jugador pudo aportar más de una evaluación si fue medido en distintos momentos postoperatorios. Las celdas con bajo número de evaluaciones deben interpretarse con cautela."
    ) +
    theme_minimal(base_size = 11) +
    theme(
      legend.position = "bottom",
      panel.grid.minor = element_blank(),
      strip.text = element_text(face = "bold"),
      axis.text.x = element_text(angle = 20, hjust = 1),
      plot.title = element_text(face = "bold"),
      plot.caption = element_text(
        hjust = 0,
        size = 8,
        color = "grey30"
      )
    )
}


# ============================================================
# 28.6) FIGURA 1
# Pico del par en extensión - implicado
# ============================================================

datos_figura_1 <- preparar_datos_boxplot(
  data = datos_analisis,
  variable_valor = "pico_del_par_extension_implicado"
)

figura_1_boxplot <- crear_boxplot_final(
  data_plot = datos_figura_1,
  titulo = "Figura 1. Pico del par en extensión del miembro implicado",
  subtitulo = "Boxplot con puntos individuales por momento postoperatorio y grupo competitivo.",
  etiqueta_y = "Pico del par en extensión (N·m)",
  agregar_linea_100 = FALSE,
  mostrar_n = FALSE
)

figura_1_boxplot



# ============================================================
# 28.7) FIGURA 2
# Pico del par en flexión - implicado
# ============================================================

datos_figura_2 <- preparar_datos_boxplot(
  data = datos_analisis,
  variable_valor = "pico_del_par_flexion_implicado"
)

figura_2_boxplot <- crear_boxplot_final(
  data_plot = datos_figura_2,
  titulo = "Figura 2. Pico del par en flexión del miembro implicado",
  subtitulo = "Boxplot con puntos individuales por momento postoperatorio y grupo competitivo.",
  etiqueta_y = "Pico del par en flexión (N·m)",
  agregar_linea_100 = FALSE,
  mostrar_n = FALSE
)

figura_2_boxplot



# ============================================================
# 28.8) FIGURA 3
# Simetría del pico del par en extensión
# ============================================================

datos_figura_3 <- preparar_datos_boxplot(
  data = datos_analisis,
  variable_valor = "simetria_pico_par_extension"
)

figura_3_boxplot <- crear_boxplot_final(
  data_plot = datos_figura_3,
  titulo = "Figura 3. Simetría del pico del par en extensión",
  subtitulo = "Boxplot con puntos individuales por momento postoperatorio y grupo competitivo. La línea discontinua indica 100% de simetría.",
  etiqueta_y = "Simetría del pico del par en extensión (%)",
  agregar_linea_100 = TRUE,
  mostrar_n = FALSE
)

figura_3_boxplot



# ============================================================
# 28.9) FIGURA 4
# Simetría del pico del par en flexión
# ============================================================

datos_figura_4 <- preparar_datos_boxplot(
  data = datos_analisis,
  variable_valor = "simetria_pico_par_flexion"
)

figura_4_boxplot <- crear_boxplot_final(
  data_plot = datos_figura_4,
  titulo = "Figura 4. Simetría del pico del par en flexión",
  subtitulo = "Boxplot con puntos individuales por momento postoperatorio y grupo competitivo. La línea discontinua indica 100% de simetría.",
  etiqueta_y = "Simetría del pico del par en flexión (%)",
  agregar_linea_100 = TRUE,
  mostrar_n = FALSE
)

figura_4_boxplot


# ============================================================
# 28.10) Tabla de n por figura y estrato
# ============================================================

tabla_n_figuras <- bind_rows(
  crear_tabla_n_figura(
    datos_figura_1,
    "Figura 1. Pico del par en extensión del miembro implicado"
  ),
  crear_tabla_n_figura(
    datos_figura_2,
    "Figura 2. Pico del par en flexión del miembro implicado"
  ),
  crear_tabla_n_figura(
    datos_figura_3,
    "Figura 3. Simetría del pico del par en extensión"
  ),
  crear_tabla_n_figura(
    datos_figura_4,
    "Figura 4. Simetría del pico del par en flexión"
  )
) %>%
  arrange(
    figura,
    velocidad_etiqueta,
    categoria_momento_pop_derivada,
    grupo_competencia
  )

tabla_n_figuras


# Resumen de celdas con bajo n
tabla_n_figuras %>%
  count(
    figura,
    bajo_n,
    name = "n_celdas"
  )


# ============================================================
# 28.11) Guardar figuras y tabla de n
# ============================================================

ggsave(
  filename = file.path(
    carpeta_figuras_boxplot,
    "Figura_1_pico_par_extension_implicado_boxplot.png"
  ),
  plot = figura_1_boxplot,
  width = 15,
  height = 8,
  dpi = 300
)

ggsave(
  filename = file.path(
    carpeta_figuras_boxplot,
    "Figura_2_pico_par_flexion_implicado_boxplot.png"
  ),
  plot = figura_2_boxplot,
  width = 15,
  height = 8,
  dpi = 300
)

ggsave(
  filename = file.path(
    carpeta_figuras_boxplot,
    "Figura_3_simetria_pico_par_extension_boxplot.png"
  ),
  plot = figura_3_boxplot,
  width = 15,
  height = 8,
  dpi = 300
)

ggsave(
  filename = file.path(
    carpeta_figuras_boxplot,
    "Figura_4_simetria_pico_par_flexion_boxplot.png"
  ),
  plot = figura_4_boxplot,
  width = 15,
  height = 8,
  dpi = 300
)



# Guardar tabla de n por estrato
if(!requireNamespace("writexl", quietly = TRUE)){
  install.packages("writexl")
}

writexl::write_xlsx(
  tabla_n_figuras,
  path = file.path(
    carpeta_figuras_boxplot,
    "Tabla_n_por_figura_y_estrato.xlsx"
  )
)

carpeta_figuras_boxplot
[1] "C:/Users/Juan_Cruz/Desktop/Cosas Juan kinesiologia/Fuerza isocinetica futbol argentino/Isocinesia/figuras_isocinesia_RLCA/boxplots_finales"
LS0tDQp0aXRsZTogIkluZm9ybWUgZXN0YWTDrXN0aWNvIGRlc2NyaXB0aXZvOiBmdWVyemEgaXNvY2luw6l0aWNhIGVuIGZ1dGJvbGlzdGFzIGNvbiBSTENBIg0KYXV0aG9yOiAiQW7DoWxpc2lzIGRlc2NyaXB0aXZvIGRlIGJhc2UgcmV0cm9zcGVjdGl2YSINCmRhdGU6ICJgciBmb3JtYXQoU3lzLkRhdGUoKSwgJyVkLyVtLyVZJylgIg0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KICAgIGRmX3ByaW50OiBwYWdlZA0KLS0tDQoNCiMgMS4gUHJvcMOzc2l0byBkZWwgYW7DoWxpc2lzDQoNCkVsIHByZXNlbnRlIGluZm9ybWUgZGVzY3JpYmUgbGEgYmFzZSBkZSBkYXRvcyBkZSBldmFsdWFjaW9uZXMgZGUgZnVlcnphIGlzb2NpbsOpdGljYSBlbiBqdWdhZG9yZXMgZGUgZsO6dGJvbCBhcmdlbnRpbm8gY29uIHJlY29uc3RydWNjacOzbiBkZWwgbGlnYW1lbnRvIGNydXphZG8gYW50ZXJpb3IgKFJMQ0EpLg0KDQpFbCBhbsOhbGlzaXMgc2UgcGxhbnRlw7MgY29uIHVuIGVuZm9xdWUgKipkZXNjcmlwdGl2byoqLCBvcmllbnRhZG8gYToNCg0KMS4gIERlcHVyYXIgeSBvcmdhbml6YXIgbGEgYmFzZSBkZSBkYXRvcy4NCjIuICBEZWZpbmlyIHVuYSBjb2hvcnRlIGFuYWzDrXRpY2EgY29uc2lzdGVudGUuDQozLiAgQ2FyYWN0ZXJpemFyIGEgbG9zIGp1Z2Fkb3JlcyB5IGxhcyBldmFsdWFjaW9uZXMgaW5jbHVpZGFzLg0KNC4gIERlc2NyaWJpciBsb3MgdmFsb3JlcyBkZSBmdWVyemEgaXNvY2luw6l0aWNhIHNlZ8O6biBuaXZlbCBjb21wZXRpdGl2byB5IHZlbG9jaWRhZCBkZSBldmFsdWFjacOzbi4NCjUuICBHZW5lcmFyIHRhYmxhcyB5IGZpZ3VyYXMgdXRpbGl6YWJsZXMgcGFyYSBsYSByZWRhY2Npw7NuIGRlIHJlc3VsdGFkb3MuDQoNCk5vIHNlIHJlYWxpemFyb24gY29tcGFyYWNpb25lcyBpbmZlcmVuY2lhbGVzIG5pIHBydWViYXMgZGUgaGlww7N0ZXNpcyBlbiBlc3RhIGV0YXBhLCBkYWRvIHF1ZSBlbCBvYmpldGl2byBwcmluY2lwYWwgZGVsIHRyYWJham8gZXMgZGVzY3JpcHRpdm8uDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIDIuIENhcmdhIGRlIHBhcXVldGVzIHkgY29uZmlndXJhY2nDs24gZ2VuZXJhbA0KDQpgYGB7ciBwYXF1ZXRlcywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDApIFBBUVVFVEVTDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KDQpwYXF1ZXRlcyA8LSBjKA0KICAicmVhZHhsIiwNCiAgImRwbHlyIiwNCiAgInRpZHlyIiwNCiAgInN0cmluZ3IiLA0KICAic3RyaW5naSIsDQogICJsdWJyaWRhdGUiLA0KICAicmVhZHIiLA0KICAiamFuaXRvciIsDQogICJnZ3Bsb3QyIiwNCiAgIm5hbmlhciIsDQogICJ2aXNkYXQiLA0KICAic2tpbXIiLA0KICAicHVycnIiLA0KICAiZm9yY2F0cyIsDQogICJmbGV4dGFibGUiLA0KICAib2ZmaWNlciINCikNCg0KaW5zdGFsYXIgPC0gcGFxdWV0ZXNbIXBhcXVldGVzICVpbiUgcm93bmFtZXMoaW5zdGFsbGVkLnBhY2thZ2VzKCkpXQ0KDQppZihsZW5ndGgoaW5zdGFsYXIpID4gMCl7DQogIGluc3RhbGwucGFja2FnZXMoaW5zdGFsYXIpDQp9DQoNCmludmlzaWJsZShsYXBwbHkocGFxdWV0ZXMsIGxpYnJhcnksIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgMy4gQ2FyZ2EgZGUgbGEgYmFzZSBvcmlnaW5hbA0KDQpMYSBiYXNlIHNlIGNhcmdhIGRlc2RlIHVuIGFyY2hpdm8gRXhjZWwuIFRvZGFzIGxhcyBjb2x1bW5hcyBzZSBpbXBvcnRhbiBpbmljaWFsbWVudGUgY29tbyB0ZXh0byBwYXJhIGV2aXRhciBwcm9ibGVtYXMgZGVyaXZhZG9zIGRlOg0KDQotICAgZmVjaGFzIGd1YXJkYWRhcyBlbiBmb3JtYXRvcyBkaXN0aW50b3M7DQotICAgY2VsZGFzIGNvbiBsYSBsZXllbmRhIOKAnERhdG8gZmFsdGFudGXigJ07DQotICAgY29sdW1uYXMgbnVtw6lyaWNhcyBtZXpjbGFkYXMgY29uIHRleHRvOw0KLSAgIGRpZmVyZW5jaWFzIGRlIGNvZGlmaWNhY2nDs24gZW50cmUgY2F0ZWdvcsOtYXMuDQoNCmBgYHtyIGNhcmdhX2Jhc2V9DQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAxKSBDQVJHQSBERSBMQSBCQVNFDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KDQpydXRhIDwtICJDOi9Vc2Vycy9KdWFuX0NydXovRGVza3RvcC9Db3NhcyBKdWFuIGtpbmVzaW9sb2dpYS9GdWVyemEgaXNvY2luZXRpY2EgZnV0Ym9sIGFyZ2VudGluby9Jc29jaW5lc2lhX1JMQ0FfQW5hbGl6YXIueGxzeCINCg0KIyBMZWVtb3MgcHJpbWVybyBzb2xvIGVuY2FiZXphZG9zIHBhcmEgY29ub2NlciBsYSBjYW50aWRhZCBkZSBjb2x1bW5hcw0Kbl9jb2x1bW5hcyA8LSByZWFkeGw6OnJlYWRfZXhjZWwoDQogIHBhdGggPSBydXRhLA0KICBzaGVldCA9ICJEYXRvcyBJc29jaW5lc2lhIiwNCiAgbl9tYXggPSAwDQopICU+JQ0KICBuY29sKCkNCg0KIyBDYXJnYW1vcyBUT0RPIGNvbW8gdGV4dG8NCmRhdG9zX3JhdyA8LSByZWFkeGw6OnJlYWRfZXhjZWwoDQogIHBhdGggPSBydXRhLA0KICBzaGVldCA9ICJEYXRvcyBJc29jaW5lc2lhIiwNCiAgY29sX3R5cGVzID0gcmVwKCJ0ZXh0Iiwgbl9jb2x1bW5hcykNCikgJT4lDQogIGphbml0b3I6OmNsZWFuX25hbWVzKCkNCg0KbmFtZXMoZGF0b3NfcmF3KQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIDQuIFJlbm9tYnJhZG8gZGUgY29sdW1uYXMNCg0KU2UgYWNvcnRhbiBsb3Mgbm9tYnJlcyBkZSBkb3MgdmFyaWFibGVzIHBhcmEgZmFjaWxpdGFyIHN1IHVzbyBwb3N0ZXJpb3IuDQoNCmBgYHtyIHJlbm9tYnJhcl9jb2x1bW5hc30NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDIpIFJFTk9NQlJBUiBDT0xVTU5BUyBDTEFWRQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KbmFtZXMoZGF0b3NfcmF3KVtuYW1lcyhkYXRvc19yYXcpID09ICJtb21lbnRvX2RlX2V2YWx1YWNpb25fcG9wX2VuX21lc2VzIl0gPC0gDQogICJtb21lbnRvX3BvcF9tZXNlcyINCg0KbmFtZXMoZGF0b3NfcmF3KVtzdHJfZGV0ZWN0KA0KICBuYW1lcyhkYXRvc19yYXcpLA0KICAiXmNhdGVnb3JpYXNfbW9tZW50b19kZV9ldmFsdWFjaW9uIg0KKV0gPC0gImNhdGVnb3JpYV9tb21lbnRvX3BvcF9vcmlnaW5hbCINCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyA1LiBGdW5jaW9uZXMgYXV4aWxpYXJlcyBwYXJhIGxpbXBpZXphIHkgdHJhbnNmb3JtYWNpw7NuDQoNClNlIGRlZmluZW4gZnVuY2lvbmVzIHBhcmE6DQoNCi0gICByZWNvbm9jZXIgZmFsdGFudGVzOw0KLSAgIGVzdGFuZGFyaXphciB0ZXh0bzsNCi0gICB0cmFuc2Zvcm1hciBmZWNoYXM7DQotICAgY29udmVydGlyIG7Dum1lcm9zOw0KLSAgIG5vcm1hbGl6YXIgdmFyaWFibGVzIGNhdGVnw7NyaWNhczsNCi0gICBjYWxjdWxhciBzaW1ldHLDrWFzIGVudHJlIG1pZW1icm8gaW1wbGljYWRvIHkgbm8gaW1wbGljYWRvLg0KDQpgYGB7ciBmdW5jaW9uZXNfbGltcGllemF9DQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAzKSBGVU5DSU9ORVMgQVVYSUxJQVJFUyBERSBMSU1QSUVaQQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KbGltcGlhcl9mYWx0YW50ZXMgPC0gZnVuY3Rpb24oeCl7DQogIA0KICB4IDwtIGFzLmNoYXJhY3Rlcih4KQ0KICB4IDwtIHN0cmluZ3I6OnN0cl9zcXVpc2goeCkNCiAgDQogIHhbeCA9PSAiIl0gPC0gTkFfY2hhcmFjdGVyXw0KICANCiAgeF9ub3JtYWxpemFkbyA8LSB4ICU+JQ0KICAgIHN0cmluZ3I6OnN0cl90b19sb3dlcigpICU+JQ0KICAgIHN0cmluZ2k6OnN0cmlfdHJhbnNfZ2VuZXJhbCgiTGF0aW4tQVNDSUkiKQ0KICANCiAgeFt4X25vcm1hbGl6YWRvICVpbiUgYygNCiAgICAiZGF0byBmYWx0YW50ZSIsDQogICAgImRhdG9zIGZhbHRhbnRlcyIsDQogICAgInNpbiBkYXRvIiwNCiAgICAic2luIGRhdG9zIiwNCiAgICAibmEiLA0KICAgICJuL2EiLA0KICAgICJuZCIsDQogICAgInMvZCIsDQogICAgIm51bGwiDQogICldIDwtIE5BX2NoYXJhY3Rlcl8NCiAgDQogIHJldHVybih4KQ0KfQ0KDQoNCm5vcm1hbGl6YXJfdGV4dG8gPC0gZnVuY3Rpb24oeCl7DQogIA0KICB4IDwtIGxpbXBpYXJfZmFsdGFudGVzKHgpDQogIA0KICB4ICU+JQ0KICAgIHN0cmluZ3I6OnN0cl9zcXVpc2goKSAlPiUNCiAgICBzdHJpbmdyOjpzdHJfdG9fbG93ZXIoKSAlPiUNCiAgICBzdHJpbmdpOjpzdHJpX3RyYW5zX2dlbmVyYWwoIkxhdGluLUFTQ0lJIikNCn0NCg0KDQpwYXJzZV9mZWNoYV9taXh0YSA8LSBmdW5jdGlvbih4KXsNCiAgDQogIHggPC0gbGltcGlhcl9mYWx0YW50ZXMoeCkNCiAgeF9udW0gPC0gc3VwcHJlc3NXYXJuaW5ncyhhcy5udW1lcmljKHgpKQ0KICANCiAgc2FsaWRhIDwtIHJlcChhcy5EYXRlKE5BKSwgbGVuZ3RoKHgpKQ0KICANCiAgaWR4X2V4Y2VsIDwtICFpcy5uYSh4X251bSkgJiB4X251bSA+IDIwMDAwICYgeF9udW0gPCA4MDAwMA0KICANCiAgc2FsaWRhW2lkeF9leGNlbF0gPC0gYXMuRGF0ZSgNCiAgICB4X251bVtpZHhfZXhjZWxdLA0KICAgIG9yaWdpbiA9ICIxODk5LTEyLTMwIg0KICApDQogIA0KICBpZHhfdGV4dG8gPC0gIWlkeF9leGNlbCAmICFpcy5uYSh4KQ0KICANCiAgc2FsaWRhW2lkeF90ZXh0b10gPC0gc3VwcHJlc3NXYXJuaW5ncygNCiAgICBsdWJyaWRhdGU6Om1keSh4W2lkeF90ZXh0b10pDQogICkNCiAgDQogIHJldHVybihzYWxpZGEpDQp9DQoNCg0KcGFyc2VfbnVtIDwtIGZ1bmN0aW9uKHgpew0KICANCiAgcmVhZHI6OnBhcnNlX251bWJlcigNCiAgICBsaW1waWFyX2ZhbHRhbnRlcyh4KSwNCiAgICBsb2NhbGUgPSByZWFkcjo6bG9jYWxlKA0KICAgICAgZGVjaW1hbF9tYXJrID0gIi4iLA0KICAgICAgZ3JvdXBpbmdfbWFyayA9ICIsIg0KICAgICkNCiAgKQ0KfQ0KDQoNCm5vcm1hbGl6YXJfc2lfbm8gPC0gZnVuY3Rpb24oeCl7DQogIA0KICB4X25vcm0gPC0gbm9ybWFsaXphcl90ZXh0byh4KQ0KICANCiAgc2FsaWRhIDwtIGRwbHlyOjpjYXNlX3doZW4oDQogICAgaXMubmEoeF9ub3JtKSB+IE5BX2NoYXJhY3Rlcl8sDQogICAgeF9ub3JtICVpbiUgYygic2kiLCAicyIsICJ5ZXMiLCAieSIsICIxIikgfiAiU8OtIiwNCiAgICB4X25vcm0gJWluJSBjKCJubyIsICJuIiwgIjAiKSB+ICJObyIsDQogICAgVFJVRSB+IE5BX2NoYXJhY3Rlcl8NCiAgKQ0KICANCiAgZmFjdG9yKHNhbGlkYSwgbGV2ZWxzID0gYygiTm8iLCAiU8OtIikpDQp9DQoNCg0Kbm9ybWFsaXphcl9pbXBsaWNhZG8gPC0gZnVuY3Rpb24oeCl7DQogIA0KICB4X25vcm0gPC0gbm9ybWFsaXphcl90ZXh0byh4KQ0KICANCiAgc2FsaWRhIDwtIGRwbHlyOjpjYXNlX3doZW4oDQogICAgaXMubmEoeF9ub3JtKSB+IE5BX2NoYXJhY3Rlcl8sDQogICAgeF9ub3JtID09ICJkZXJlY2hvIiB+ICJEZXJlY2hvIiwNCiAgICB4X25vcm0gPT0gIml6cXVpZXJkbyIgfiAiSXpxdWllcmRvIiwNCiAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXw0KICApDQogIA0KICBmYWN0b3Ioc2FsaWRhLCBsZXZlbHMgPSBjKCJEZXJlY2hvIiwgIkl6cXVpZXJkbyIpKQ0KfQ0KDQoNCm5vcm1hbGl6YXJfaW5qZXJ0byA8LSBmdW5jdGlvbih4KXsNCiAgDQogIHhfbm9ybSA8LSBub3JtYWxpemFyX3RleHRvKHgpDQogIA0KICBzYWxpZGEgPC0gZHBseXI6OmNhc2Vfd2hlbigNCiAgICBpcy5uYSh4X25vcm0pIH4gTkFfY2hhcmFjdGVyXywNCiAgICB4X25vcm0gPT0gImN1YWRyaWNpcGl0YWwiIH4gIkN1YWRyaWNpcGl0YWwiLA0KICAgIHhfbm9ybSA9PSAiaHRoIiB+ICJIVEgiLA0KICAgIHhfbm9ybSA9PSAic3RyaSIgfiAiU1RSSSIsDQogICAgVFJVRSB+IE5BX2NoYXJhY3Rlcl8NCiAgKQ0KICANCiAgZmFjdG9yKA0KICAgIHNhbGlkYSwNCiAgICBsZXZlbHMgPSBjKCJIVEgiLCAiU1RSSSIsICJDdWFkcmljaXBpdGFsIikNCiAgKQ0KfQ0KDQoNCm5vcm1hbGl6YXJfY2F0ZWdvcmlhX21vbWVudG8gPC0gZnVuY3Rpb24oeCl7DQogIA0KICB4X25vcm0gPC0gbm9ybWFsaXphcl90ZXh0byh4KQ0KICANCiAgc2FsaWRhIDwtIGRwbHlyOjpjYXNlX3doZW4oDQogICAgaXMubmEoeF9ub3JtKSB+IE5BX2NoYXJhY3Rlcl8sDQogICAgc3RyX2RldGVjdCh4X25vcm0sICJtZW5vciIpICYgc3RyX2RldGVjdCh4X25vcm0sICI2IikgfiAiPDYgbWVzZXMiLA0KICAgIHN0cl9kZXRlY3QoeF9ub3JtLCAiNiIpICYgc3RyX2RldGVjdCh4X25vcm0sICI4IikgfiAiNi04IG1lc2VzIiwNCiAgICBzdHJfZGV0ZWN0KHhfbm9ybSwgIm1heW9yIikgJiBzdHJfZGV0ZWN0KHhfbm9ybSwgIjgiKSB+ICI+OCBtZXNlcyIsDQogICAgVFJVRSB+IE5BX2NoYXJhY3Rlcl8NCiAgKQ0KICANCiAgZmFjdG9yKA0KICAgIHNhbGlkYSwNCiAgICBsZXZlbHMgPSBjKCI8NiBtZXNlcyIsICI2LTggbWVzZXMiLCAiPjggbWVzZXMiKQ0KICApDQp9DQoNCg0KcmVkb25kZWFyX21lc2VzIDwtIGZ1bmN0aW9uKHgpew0KICBmbG9vcih4ICsgMC41KQ0KfQ0KDQoNCmNhbGN1bGFyX3NpbWV0cmlhIDwtIGZ1bmN0aW9uKHZhbG9yX2ltcGxpY2FkbywgdmFsb3Jfbm9faW1wbGljYWRvKXsNCiAgDQogIGRwbHlyOjppZl9lbHNlKA0KICAgIGlzLm5hKHZhbG9yX2ltcGxpY2FkbykgfA0KICAgICAgaXMubmEodmFsb3Jfbm9faW1wbGljYWRvKSB8DQogICAgICB2YWxvcl9ub19pbXBsaWNhZG8gPT0gMCwNCiAgICBOQV9yZWFsXywNCiAgICAodmFsb3JfaW1wbGljYWRvIC8gdmFsb3Jfbm9faW1wbGljYWRvKSAqIDEwMA0KICApDQp9DQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgNi4gSWRlbnRpZmljYWNpw7NuIGRlIHZhcmlhYmxlcyBudW3DqXJpY2FzDQoNClNlIG9yZ2FuaXphcm9uIGxhcyBjb2x1bW5hcyBxdWUgY29ycmVzcG9uZGVuIGE6DQoNCi0gICB2YXJpYWJsZXMgZGUgcGVzbyB5IGFsdHVyYTsNCi0gICBtb21lbnRvIHBvc3RvcGVyYXRvcmlvOw0KLSAgIG3DqXRyaWNhcyBkZSBmdWVyemE7DQotICAgbsO6bWVybyBkZSByZXBldGljacOzbiBkZSB0cmFiYWpvIG3DoXhpbW8uDQoNCmBgYHtyIHZhcmlhYmxlc19udW1lcmljYXN9DQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyA0KSBJREVOVElGSUNBUiBHUlVQT1MgREUgVkFSSUFCTEVTIE5VTcOJUklDQVMNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNCnZhcnNfbWV0cmljYXNfY29udGludWFzIDwtIG5hbWVzKGRhdG9zX3Jhdylbc3RyX2RldGVjdCgNCiAgbmFtZXMoZGF0b3NfcmF3KSwNCiAgIl4ocGljb19kZWxfcGFyfHRyYWJqX3Blc29fY29ycG9yYWx8dHJhYmFqb190b3RhbHxyYW5nb19kZV9tb3Z8cmF6b25fYWdvbl9hbnRhZ29uKSINCildDQoNCnZhcnNfcmVwZXRpY2lvbmVzIDwtIG5hbWVzKGRhdG9zX3Jhdylbc3RyX2RldGVjdCgNCiAgbmFtZXMoZGF0b3NfcmF3KSwNCiAgInJlcF90cmFiYWpvX21heCINCildDQoNCnZhcnNfbnVtZXJpY2FzIDwtIHVuaXF1ZShjKA0KICAiYWx0dXJhIiwNCiAgInBlc28iLA0KICAibW9tZW50b19wb3BfbWVzZXMiLA0KICB2YXJzX21ldHJpY2FzX2NvbnRpbnVhcywNCiAgdmFyc19yZXBldGljaW9uZXMNCikpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgNy4gTGltcGllemEgZGUgYmFzZSBkZSBkYXRvcw0KDQpFbiBlc3RhIGV0YXBhIHNlOg0KDQotICAgZWxpbWluYXJvbiBjb2x1bW5hcyBubyB1dGlsaXphZGFzIGVuIGVsIGFuw6FsaXNpczsNCi0gICBjb252ZXJzaW9uIGRlIHZhcmlhYmxlcyBudW3DqXJpY2FzOw0KLSAgIHRyYW5zZm9ybWFjacOzbiBkZSBmZWNoYXM7DQotICAgY2FsY3VsbyBkZSBlZGFkIGFsIG1vbWVudG8gZGUgbGEgZXZhbHVhY2nDs247DQotICAgbm9ybWFsaXphY2nDs24gZGUgdmFyaWFibGVzIGNhdGVnb3LDrWFzOw0KLSAgIGdlbmVyYWNpw7NuIGRlIGxhcyBjYXRlZ29yw61hcyB0ZW1wb3JhbGVzIGRlbCBtb21lbnRvIHBvc3RxdWlyw7pyZ2ljby4NCg0KTGFzIHZlbnRhbmFzIHRlbXBvcmFsZXMgZnVlcm9uOg0KDQotICAgKipcPDYgbWVzZXMqKg0KLSAgICoqNiBhIFw8OCBtZXNlcyoqDQotICAgKiriiaU4IG1lc2VzKioNCg0KYGBge3IgY29uc3RydWlyX2Jhc2VfbGltcGlhfQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgNSkgQ09OU1RSVUlSIEJBU0UgTElNUElBDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KDQpkYXRvc19pc28gPC0gZGF0b3NfcmF3ICU+JQ0KICANCiAgbXV0YXRlKGFjcm9zcyhldmVyeXRoaW5nKCksIGxpbXBpYXJfZmFsdGFudGVzKSkgJT4lDQogIA0KICBzZWxlY3QoDQogICAgLWFueV9vZihjKA0KICAgICAgIm5vbWJyZSIsDQogICAgICAic2V4byIsDQogICAgICAidGVyYXBldXRhIiwNCiAgICAgICJyZWZlcmVudGUiLA0KICAgICAgImFydGljdWxhY2lvbiIsDQogICAgICAiZGlhZ25vc3RpY28iLA0KICAgICAgImVkYWRfYWxfbW9tZW50b19kZV9ldmFsdWFyIg0KICAgICkpDQogICkgJT4lDQogIA0KICBtdXRhdGUoDQogICAgYWNyb3NzKA0KICAgICAgYWxsX29mKGludGVyc2VjdCh2YXJzX251bWVyaWNhcywgbmFtZXMoLikpKSwNCiAgICAgIHBhcnNlX251bQ0KICAgICkNCiAgKSAlPiUNCiAgDQogIG11dGF0ZSgNCiAgICANCiAgICBpZCA9IGFzLmNoYXJhY3RlcihpZCksDQogICAgDQogICAgbmFjaWRvID0gcGFyc2VfZmVjaGFfbWl4dGEobmFjaWRvKSwNCiAgICBzZXNpb24gPSBwYXJzZV9mZWNoYV9taXh0YShzZXNpb24pLA0KICAgIA0KICAgIGVkYWRfZXZhbHVhY2lvbiA9IGlmX2Vsc2UoDQogICAgICBpcy5uYShuYWNpZG8pIHwgaXMubmEoc2VzaW9uKSwNCiAgICAgIE5BX2ludGVnZXJfLA0KICAgICAgYXMuaW50ZWdlcigNCiAgICAgICAgZmxvb3IoDQogICAgICAgICAgbHVicmlkYXRlOjp0aW1lX2xlbmd0aCgNCiAgICAgICAgICAgIGx1YnJpZGF0ZTo6aW50ZXJ2YWwobmFjaWRvLCBzZXNpb24pLA0KICAgICAgICAgICAgdW5pdCA9ICJ5ZWFycyINCiAgICAgICAgICApDQogICAgICAgICkNCiAgICAgICkNCiAgICApLA0KICAgIA0KICAgIG1vbWVudG9fcG9wX21lc2VzX3JlZG9uZGVhZG8gPSByZWRvbmRlYXJfbWVzZXMobW9tZW50b19wb3BfbWVzZXMpLA0KICAgIA0KICAgIGltcGxpY2FkbyA9IG5vcm1hbGl6YXJfaW1wbGljYWRvKGltcGxpY2FkbyksDQogICAgbml2ZWxfZGVfY29tcGV0ZW5jaWEgPSBmYWN0b3Iobml2ZWxfZGVfY29tcGV0ZW5jaWEpLA0KICAgIGNhdGVnb3JpYV9tb21lbnRvX3BvcF9vcmlnaW5hbCA9IG5vcm1hbGl6YXJfY2F0ZWdvcmlhX21vbWVudG8oDQogICAgICBjYXRlZ29yaWFfbW9tZW50b19wb3Bfb3JpZ2luYWwNCiAgICApLA0KICAgIGluamVydG8gPSBub3JtYWxpemFyX2luamVydG8oaW5qZXJ0byksDQogICAgbGVtYWlyZV9yZWZ1ZXJ6b19sYXRlcmFsID0gbm9ybWFsaXphcl9zaV9ubyhsZW1haXJlX3JlZnVlcnpvX2xhdGVyYWwpLA0KICAgIHJlbGVzaW9uID0gbm9ybWFsaXphcl9zaV9ubyhyZWxlc2lvbiksDQogICAgdmVsb2NpZGFkID0gZmFjdG9yKHZlbG9jaWRhZCksDQogICAgDQogICAgY2F0ZWdvcmlhX21vbWVudG9fcG9wX2Rlcml2YWRhID0gZmFjdG9yKA0KICAgICAgY2FzZV93aGVuKA0KICAgICAgICBpcy5uYShtb21lbnRvX3BvcF9tZXNlcykgfiBOQV9jaGFyYWN0ZXJfLA0KICAgICAgICBtb21lbnRvX3BvcF9tZXNlcyA8IDYgfiAiPDYgbWVzZXMiLA0KICAgICAgICBtb21lbnRvX3BvcF9tZXNlcyA+PSA2ICYgbW9tZW50b19wb3BfbWVzZXMgPCA4IH4gIjYgYSA8OCBtZXNlcyIsDQogICAgICAgIG1vbWVudG9fcG9wX21lc2VzID49IDggfiAi4omlOCBtZXNlcyINCiAgICAgICksDQogICAgICBsZXZlbHMgPSBjKCI8NiBtZXNlcyIsICI2IGEgPDggbWVzZXMiLCAi4omlOCBtZXNlcyIpDQogICAgKQ0KICApDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgOC4gQ8OhbGN1bG8gZGUgdmFyaWFibGVzIGRlIHNpbWV0csOtYQ0KDQpTZSBjYWxjdWxhcm9uIMOtbmRpY2VzIGRlIHNpbWV0csOtYSBkZSBhY3VlcmRvIGNvbiBsYSBmw7NybXVsYToNCg0KJCQNClx0ZXh0e1NpbWV0csOtYX0gPSBcZnJhY3tcdGV4dHt2YWxvciBpbXBsaWNhZG99fXtcdGV4dHt2YWxvciBubyBpbXBsaWNhZG99fSBcdGltZXMgMTAwDQokJA0KDQpTZSBnZW5lcmFyb24gc2ltZXRyw61hcyBwYXJhOg0KDQotICAgcGljbyBkZSBwYXIgZW4gZXh0ZW5zacOzbjsNCi0gICBwaWNvIGRlIHBhciBlbiBmbGV4acOzbjsNCi0gICB0cmFiYWpvL3Blc28gY29ycG9yYWwgZW4gZXh0ZW5zacOzbjsNCi0gICB0cmFiYWpvL3Blc28gY29ycG9yYWwgZW4gZmxleGnDs247DQotICAgdHJhYmFqbyB0b3RhbCBlbiBleHRlbnNpw7NuOw0KLSAgIHRyYWJham8gdG90YWwgZW4gZmxleGnDs24uDQoNCmBgYHtyIHNpbWV0cmlhc30NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDUuMSkgQ1JFQVIgVkFSSUFCTEVTIERFIFNJTUVUUsONQQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KZGF0b3NfaXNvIDwtIGRhdG9zX2lzbyAlPiUNCiAgbXV0YXRlKA0KICAgIA0KICAgIHNpbWV0cmlhX3BpY29fcGFyX2V4dGVuc2lvbiA9IGNhbGN1bGFyX3NpbWV0cmlhKA0KICAgICAgdmFsb3JfaW1wbGljYWRvID0gcGljb19kZWxfcGFyX2V4dGVuc2lvbl9pbXBsaWNhZG8sDQogICAgICB2YWxvcl9ub19pbXBsaWNhZG8gPSBwaWNvX2RlbF9wYXJfZXh0ZW5zaW9uX25vX2ltcGxpY2Fkbw0KICAgICksDQogICAgDQogICAgc2ltZXRyaWFfcGljb19wYXJfZmxleGlvbiA9IGNhbGN1bGFyX3NpbWV0cmlhKA0KICAgICAgdmFsb3JfaW1wbGljYWRvID0gcGljb19kZWxfcGFyX2ZsZXhpb25faW1wbGljYWRvLA0KICAgICAgdmFsb3Jfbm9faW1wbGljYWRvID0gcGljb19kZWxfcGFyX2ZsZXhpb25fbm9faW1wbGljYWRvDQogICAgKSwNCiAgICANCiAgICBzaW1ldHJpYV90cmFial9wZXNvX2NvcnBvcmFsX2V4dGVuc2lvbiA9IGNhbGN1bGFyX3NpbWV0cmlhKA0KICAgICAgdmFsb3JfaW1wbGljYWRvID0gdHJhYmpfcGVzb19jb3Jwb3JhbF9leHRlbnNpb25faW1wbGljYWRvLA0KICAgICAgdmFsb3Jfbm9faW1wbGljYWRvID0gdHJhYmpfcGVzb19jb3Jwb3JhbF9leHRlbnNpb25fbm9faW1wbGljYWRvDQogICAgKSwNCiAgICANCiAgICBzaW1ldHJpYV90cmFial9wZXNvX2NvcnBvcmFsX2ZsZXhpb24gPSBjYWxjdWxhcl9zaW1ldHJpYSgNCiAgICAgIHZhbG9yX2ltcGxpY2FkbyA9IHRyYWJqX3Blc29fY29ycG9yYWxfZmxleGlvbl9pbXBsaWNhZG8sDQogICAgICB2YWxvcl9ub19pbXBsaWNhZG8gPSB0cmFial9wZXNvX2NvcnBvcmFsX2ZsZXhpb25fbm9faW1wbGljYWRvDQogICAgKSwNCiAgICANCiAgICBzaW1ldHJpYV90cmFiYWpvX3RvdGFsX2V4dGVuc2lvbiA9IGNhbGN1bGFyX3NpbWV0cmlhKA0KICAgICAgdmFsb3JfaW1wbGljYWRvID0gdHJhYmFqb190b3RhbF9leHRlbnNpb25faW1wbGljYWRvLA0KICAgICAgdmFsb3Jfbm9faW1wbGljYWRvID0gdHJhYmFqb190b3RhbF9leHRlbnNpb25fbm9faW1wbGljYWRvDQogICAgKSwNCiAgICANCiAgICBzaW1ldHJpYV90cmFiYWpvX3RvdGFsX2ZsZXhpb24gPSBjYWxjdWxhcl9zaW1ldHJpYSgNCiAgICAgIHZhbG9yX2ltcGxpY2FkbyA9IHRyYWJham9fdG90YWxfZmxleGlvbl9pbXBsaWNhZG8sDQogICAgICB2YWxvcl9ub19pbXBsaWNhZG8gPSB0cmFiYWpvX3RvdGFsX2ZsZXhpb25fbm9faW1wbGljYWRvDQogICAgKQ0KICApDQoNCnZhcnNfc2ltZXRyaWFzIDwtIGMoDQogICJzaW1ldHJpYV9waWNvX3Bhcl9leHRlbnNpb24iLA0KICAic2ltZXRyaWFfcGljb19wYXJfZmxleGlvbiIsDQogICJzaW1ldHJpYV90cmFial9wZXNvX2NvcnBvcmFsX2V4dGVuc2lvbiIsDQogICJzaW1ldHJpYV90cmFial9wZXNvX2NvcnBvcmFsX2ZsZXhpb24iLA0KICAic2ltZXRyaWFfdHJhYmFqb190b3RhbF9leHRlbnNpb24iLA0KICAic2ltZXRyaWFfdHJhYmFqb190b3RhbF9mbGV4aW9uIg0KKQ0KDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgOS4gUmV2aXNpw7NuIGdlbmVyYWwgZGUgbGEgYmFzZSBjdXJhZGENCg0KYGBge3IgcmV2aXNpb25faW5pY2lhbH0NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDYpIFZFUklGSUNBQ0nDk04gSU5JQ0lBTCBERSBMQSBCQVNFIExJTVBJQQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KZ2xpbXBzZShkYXRvc19pc28pDQpzdW1tYXJ5KGRhdG9zX2lzbykNCnNraW1yOjpza2ltKGRhdG9zX2lzbykNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyAxMC4gRmFsdGFudGVzIHkgY29udHJvbGVzIGRlIGNhbGlkYWQNCg0KU2UgcmV2aXPDsyBsYSBjYW50aWRhZCBkZSBkYXRvcyBmYWx0YW50ZXMgcG9yIHZhcmlhYmxlIHkgc2UgcmVhbGl6YXJvbiBjb250cm9sZXMgZGUgY29uc2lzdGVuY2lhIHBhcmEgaWRlbnRpZmljYXI6DQoNCi0gICB2YWxvcmVzIGluY29uc2lzdGVudGVzIGVuIGNhdGVnb3LDrWFzOw0KLSAgIGR1cGxpY2Fkb3M7DQotICAgZmVjaGFzIGluY29uc2lzdGVudGVzOw0KLSAgIGVkYWRlcyBpbXByb2JhYmxlczsNCi0gICB2YWxvcmVzIGFudHJvcG9tw6l0cmljb3MgYXTDrXBpY29zOw0KLSAgIHZhbG9yZXMgbmVnYXRpdm9zIG5vIGVzcGVyYWRvczsNCi0gICByZXBldGljaW9uZXMgY29uIHZhbG9yZXMgbm8gZW50ZXJvcy4NCg0KYGBge3IgZmFsdGFudGVzX3lfY29udHJvbF9jYWxpZGFkfQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgNykgVEFCTEEgR0VORVJBTCBERSBGQUxUQU5URVMNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNCnRhYmxhX2ZhbHRhbnRlcyA8LSBkYXRvc19pc28gJT4lDQogIHN1bW1hcmlzZSgNCiAgICBhY3Jvc3MoDQogICAgICBldmVyeXRoaW5nKCksDQogICAgICB+IHN1bShpcy5uYSguKSkNCiAgICApDQogICkgJT4lDQogIHBpdm90X2xvbmdlcigNCiAgICBjb2xzID0gZXZlcnl0aGluZygpLA0KICAgIG5hbWVzX3RvID0gInZhcmlhYmxlIiwNCiAgICB2YWx1ZXNfdG8gPSAibl9mYWx0YW50ZXMiDQogICkgJT4lDQogIG11dGF0ZSgNCiAgICBwb3JjZW50YWplX2ZhbHRhbnRlcyA9IHJvdW5kKDEwMCAqIG5fZmFsdGFudGVzIC8gbnJvdyhkYXRvc19pc28pLCAxKQ0KICApICU+JQ0KICBhcnJhbmdlKGRlc2Mobl9mYWx0YW50ZXMpKQ0KDQp0YWJsYV9mYWx0YW50ZXMNCg0KbmFuaWFyOjp2aXNfbWlzcyhkYXRvc19pc28pDQp2aXNkYXQ6OnZpc19kYXQoZGF0b3NfaXNvKQ0KDQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDgpIFJFU1VNRU4gREUgVkFSSUFCTEVTIENBVEVHw5NSSUNBUw0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KdmFyc19jYXRlZ29yaWNhcyA8LSBjKA0KICAiaW1wbGljYWRvIiwNCiAgIm5pdmVsX2RlX2NvbXBldGVuY2lhIiwNCiAgImNhdGVnb3JpYV9tb21lbnRvX3BvcF9vcmlnaW5hbCIsDQogICJjYXRlZ29yaWFfbW9tZW50b19wb3BfZGVyaXZhZGEiLA0KICAiaW5qZXJ0byIsDQogICJsZW1haXJlX3JlZnVlcnpvX2xhdGVyYWwiLA0KICAicmVsZXNpb24iLA0KICAidmVsb2NpZGFkIg0KKQ0KDQpyZXN1bWVuX2NhdGVnb3JpY2FzIDwtIHB1cnJyOjptYXBfZGZyKA0KICB2YXJzX2NhdGVnb3JpY2FzLA0KICBmdW5jdGlvbih2KXsNCiAgICANCiAgICBkYXRvc19pc28gJT4lDQogICAgICBncm91cF9ieSh2YWxvciA9IC5kYXRhW1t2XV0pICU+JQ0KICAgICAgc3VtbWFyaXNlKG4gPSBuKCksIC5ncm91cHMgPSAiZHJvcCIpICU+JQ0KICAgICAgbXV0YXRlKA0KICAgICAgICB2YXJpYWJsZSA9IHYsDQogICAgICAgIHBvcmNlbnRhamUgPSByb3VuZCgxMDAgKiBuIC8gbnJvdyhkYXRvc19pc28pLCAxKQ0KICAgICAgKSAlPiUNCiAgICAgIHNlbGVjdCh2YXJpYWJsZSwgdmFsb3IsIG4sIHBvcmNlbnRhamUpDQogIH0NCikNCg0KcmVzdW1lbl9jYXRlZ29yaWNhcw0KDQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDkpIFZBTE9SRVMgTk8gRVNQRVJBRE9TIEFOVEVTIERFIExBIFJFQ09ESUZJQ0FDScOTTg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KdmFsb3Jlc19pbXBsaWNhZG9fbm9fZXNwZXJhZG9zIDwtIGRhdG9zX3JhdyAlPiUNCiAgdHJhbnNtdXRlKA0KICAgIGltcGxpY2Fkb19vcmlnaW5hbCA9IGltcGxpY2FkbywNCiAgICBpbXBsaWNhZG9fbm9ybWFsaXphZG8gPSBub3JtYWxpemFyX3RleHRvKGltcGxpY2FkbykNCiAgKSAlPiUNCiAgZmlsdGVyKA0KICAgICFpcy5uYShpbXBsaWNhZG9fbm9ybWFsaXphZG8pLA0KICAgICFpbXBsaWNhZG9fbm9ybWFsaXphZG8gJWluJSBjKCJkZXJlY2hvIiwgIml6cXVpZXJkbyIpDQogICkgJT4lDQogIGRpc3RpbmN0KCkNCg0KdmFsb3Jlc19pbXBsaWNhZG9fbm9fZXNwZXJhZG9zDQoNCg0KdmFsb3Jlc19pbmplcnRvX25vX2VzcGVyYWRvcyA8LSBkYXRvc19yYXcgJT4lDQogIHRyYW5zbXV0ZSgNCiAgICBpbmplcnRvX29yaWdpbmFsID0gaW5qZXJ0bywNCiAgICBpbmplcnRvX25vcm1hbGl6YWRvID0gbm9ybWFsaXphcl90ZXh0byhpbmplcnRvKQ0KICApICU+JQ0KICBmaWx0ZXIoDQogICAgIWlzLm5hKGluamVydG9fbm9ybWFsaXphZG8pLA0KICAgICFpbmplcnRvX25vcm1hbGl6YWRvICVpbiUgYygiaHRoIiwgInN0cmkiLCAiY3VhZHJpY2lwaXRhbCIpDQogICkgJT4lDQogIGRpc3RpbmN0KCkNCg0KdmFsb3Jlc19pbmplcnRvX25vX2VzcGVyYWRvcw0KDQoNCnZhbG9yZXNfbGVtYWlyZV9ub19lc3BlcmFkb3MgPC0gZGF0b3NfcmF3ICU+JQ0KICB0cmFuc211dGUoDQogICAgbGVtYWlyZV9vcmlnaW5hbCA9IGxlbWFpcmVfcmVmdWVyem9fbGF0ZXJhbCwNCiAgICBsZW1haXJlX25vcm1hbGl6YWRvID0gbm9ybWFsaXphcl90ZXh0byhsZW1haXJlX3JlZnVlcnpvX2xhdGVyYWwpDQogICkgJT4lDQogIGZpbHRlcigNCiAgICAhaXMubmEobGVtYWlyZV9ub3JtYWxpemFkbyksDQogICAgIWxlbWFpcmVfbm9ybWFsaXphZG8gJWluJSBjKCJzaSIsICJzIiwgInllcyIsICJ5IiwgIjEiLCAibm8iLCAibiIsICIwIikNCiAgKSAlPiUNCiAgZGlzdGluY3QoKQ0KDQp2YWxvcmVzX2xlbWFpcmVfbm9fZXNwZXJhZG9zDQoNCg0KdmFsb3Jlc19yZWxlc2lvbl9ub19lc3BlcmFkb3MgPC0gZGF0b3NfcmF3ICU+JQ0KICB0cmFuc211dGUoDQogICAgcmVsZXNpb25fb3JpZ2luYWwgPSByZWxlc2lvbiwNCiAgICByZWxlc2lvbl9ub3JtYWxpemFkbyA9IG5vcm1hbGl6YXJfdGV4dG8ocmVsZXNpb24pDQogICkgJT4lDQogIGZpbHRlcigNCiAgICAhaXMubmEocmVsZXNpb25fbm9ybWFsaXphZG8pLA0KICAgICFyZWxlc2lvbl9ub3JtYWxpemFkbyAlaW4lIGMoInNpIiwgInMiLCAieWVzIiwgInkiLCAiMSIsICJubyIsICJuIiwgIjAiKQ0KICApICU+JQ0KICBkaXN0aW5jdCgpDQoNCnZhbG9yZXNfcmVsZXNpb25fbm9fZXNwZXJhZG9zDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgMTEuIEJhc2UgYSBuaXZlbCBkZSBldmFsdWFjacOzbg0KDQpMYSBiYXNlIG9yaWdpbmFsIGNvbnRpZW5lIHVuYSBmaWxhIHBvcjoNCg0KLSAgIGp1Z2Fkb3I7DQotICAgZmVjaGEgZGUgc2VzacOzbjsNCi0gICB2ZWxvY2lkYWQgZGUgZXZhbHVhY2nDs24uDQoNClBhcmEgY2FyYWN0ZXJpemFyIGxhIG11ZXN0cmEgeSBjb25zdHJ1aXIgZWwgZmx1am8gZGUgc2VsZWNjacOzbiwgc2UgZ2VuZXLDsyB1bmEgYmFzZSBjb24gKip1bmEgZmlsYSBwb3IgZXZhbHVhY2nDs24qKjoNCg0KJCQNClx0ZXh0e2V2YWx1YWNpw7NufSA9IFx0ZXh0e0lEIGRlbCBqdWdhZG9yfSBcdGltZXMgXHRleHR7ZmVjaGEgZGUgc2VzacOzbn0NCiQkDQoNCmBgYHtyIGJhc2VfZXZhbHVhY2lvbmVzfQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMTApIEJBU0UgQSBOSVZFTCBERSBFVkFMVUFDScOTTg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KZXZhbHVhY2lvbmVzX2lzbyA8LSBkYXRvc19pc28gJT4lDQogIGRpc3RpbmN0KA0KICAgIGlkLA0KICAgIHNlc2lvbiwNCiAgICAua2VlcF9hbGwgPSBUUlVFDQogICkNCg0KZXZhbHVhY2lvbmVzX2lzbw0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIDEyLiBGbHVqbyBkZSBzZWxlY2Npw7NuIGRlIGxhIGNvaG9ydGUgYW5hbMOtdGljYQ0KDQpQYXJhIGVsIGFuw6FsaXNpcyBkZXNjcmlwdGl2byBmaW5hbCBzZSBhcGxpY2Fyb24gbG9zIHNpZ3VpZW50ZXMgY3JpdGVyaW9zOg0KDQoxLiAgRXhjbHVpciBqdWdhZG9yZXMgY29uIGFsIG1lbm9zIHVuIHJlZ2lzdHJvIGRlICoqcmVsZXNpw7NuID0gU8OtKiouDQoyLiAgRXhjbHVpciBldmFsdWFjaW9uZXMgc2luIGRhdG8gZGlzcG9uaWJsZSBkZSBtb21lbnRvIHBvc3RvcGVyYXRvcmlvLg0KMy4gIEV4Y2x1aXIgZXZhbHVhY2lvbmVzIHJlYWxpemFkYXMgYSBtw6FzIGRlICoqMTggbWVzZXMqKiBkZSBsYSBjaXJ1Z8OtYS4NCjQuICBFeGNsdWlyIHJlZ2lzdHJvcyBzaW4gcG9zaWJpbGlkYWQgZGUgYXNpZ25hciBncnVwbyBjb21wZXRpdGl2by4NCg0KTGEgZXhjbHVzacOzbiBwb3IgcmVsZXNpw7NuIHNlIGFwbGljw7MgKiphIG5pdmVsIGRlbCBqdWdhZG9yKiosIHlhIHF1ZSBsYSByZWxlc2nDs24gcmVwcmVzZW50YSB1bmEgY2FyYWN0ZXLDrXN0aWNhIGNsw61uaWNhIGRlbCBjYXNvIHkgbm8gZGUgdW5hIGZpbGEgYWlzbGFkYS4NCg0KYGBge3IgZmx1am9fZXhjbHVzaW9uZXN9DQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAxMSkgRkxVSk8gREUgU0VMRUNDScOTTiBERSBMQSBDT0hPUlRFIEFOQUzDjVRJQ0ENCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNCiMgSnVnYWRvcmVzIGNvbiBhbCBtZW5vcyB1bmEgcmVsZXNpw7NuIHJlZ2lzdHJhZGENCmlkc19yZWxlc2lvbl9zaSA8LSBkYXRvc19pc28gJT4lDQogIGdyb3VwX2J5KGlkKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIHJlbGVzaW9uX3NpID0gYW55KHJlbGVzaW9uID09ICJTw60iLCBuYS5ybSA9IFRSVUUpLA0KICAgIC5ncm91cHMgPSAiZHJvcCINCiAgKSAlPiUNCiAgZmlsdGVyKHJlbGVzaW9uX3NpKSAlPiUNCiAgcHVsbChpZCkNCg0KDQojIEV0YXBhIDE6IGJhc2UgaW5pY2lhbCBhIG5pdmVsIGRlIGV2YWx1YWNpw7NuDQpldmFsdWFjaW9uZXNfaW5pY2lhbGVzIDwtIGV2YWx1YWNpb25lc19pc28NCg0KDQojIEV0YXBhIDI6IGV4Y2x1aXIganVnYWRvcmVzIGNvbiByZWxlc2nDs24NCmV2YWx1YWNpb25lc19zaW5fcmVsZXNpb24gPC0gZXZhbHVhY2lvbmVzX2luaWNpYWxlcyAlPiUNCiAgZmlsdGVyKCFpZCAlaW4lIGlkc19yZWxlc2lvbl9zaSkNCg0KDQojIEV0YXBhIDM6IGV4Y2x1aXIgZXZhbHVhY2lvbmVzIHNpbiBtb21lbnRvIFBPUA0KZXZhbHVhY2lvbmVzX2Nvbl9wb3AgPC0gZXZhbHVhY2lvbmVzX3Npbl9yZWxlc2lvbiAlPiUNCiAgZmlsdGVyKCFpcy5uYShtb21lbnRvX3BvcF9tZXNlcykpDQoNCg0KIyBFdGFwYSA0OiBleGNsdWlyIGV2YWx1YWNpb25lcyA+MTggbWVzZXMNCmV2YWx1YWNpb25lc19oYXN0YV8xOG0gPC0gZXZhbHVhY2lvbmVzX2Nvbl9wb3AgJT4lDQogIGZpbHRlcihtb21lbnRvX3BvcF9tZXNlcyA8PSAxOCkNCg0KDQojIEV0YXBhIDU6IGNyZWFyIGdydXBvIGNvbXBldGl0aXZvIHkgZXhjbHVpciBubyBjbGFzaWZpY2FibGVzDQpldmFsdWFjaW9uZXNfYW5hbGlzaXMgPC0gZXZhbHVhY2lvbmVzX2hhc3RhXzE4bSAlPiUNCiAgbXV0YXRlKA0KICAgIGdydXBvX2NvbXBldGVuY2lhID0gY2FzZV93aGVuKA0KICAgICAgbml2ZWxfZGVfY29tcGV0ZW5jaWEgPT0gIlByaW1lcmEgZGl2aXNpw7NuIiB+ICJQcmltZXJhIiwNCiAgICAgIG5pdmVsX2RlX2NvbXBldGVuY2lhID09ICJTZWd1bmRhIGRpdmlzacOzbiIgfiAiU2VndW5kYSIsDQogICAgICBuaXZlbF9kZV9jb21wZXRlbmNpYSAlaW4lIGMoDQogICAgICAgICJUZXJjZXJhIERpdmlzacOzbiIsDQogICAgICAgICJBbWF0ZXVyIg0KICAgICAgKSB+ICJUZXJjZXJhICsgQW1hdGV1ciIsDQogICAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXw0KICAgICksDQogICAgDQogICAgZ3J1cG9fY29tcGV0ZW5jaWEgPSBmYWN0b3IoDQogICAgICBncnVwb19jb21wZXRlbmNpYSwNCiAgICAgIGxldmVscyA9IGMoDQogICAgICAgICJQcmltZXJhIiwNCiAgICAgICAgIlNlZ3VuZGEiLA0KICAgICAgICAiVGVyY2VyYSArIEFtYXRldXIiDQogICAgICApDQogICAgKQ0KICApICU+JQ0KICBmaWx0ZXIoIWlzLm5hKGdydXBvX2NvbXBldGVuY2lhKSkNCg0KDQojIEZ1bmNpw7NuIGF1eGlsaWFyIHBhcmEgcmVzdW1pciBjYWRhIGV0YXBhDQpyZXN1bWlyX2V0YXBhX2ZsdWpvIDwtIGZ1bmN0aW9uKGRhdGEsIGV0YXBhKXsNCiAgDQogIHRpYmJsZTo6dGliYmxlKA0KICAgIGV0YXBhID0gZXRhcGEsDQogICAgbl9qdWdhZG9yZXMgPSBuX2Rpc3RpbmN0KGRhdGEkaWQpLA0KICAgIG5fZXZhbHVhY2lvbmVzID0gbnJvdyhkYXRhKQ0KICApDQp9DQoNCg0KZmx1am9fZXhjbHVzaW9uZXMgPC0gYmluZF9yb3dzKA0KICByZXN1bWlyX2V0YXBhX2ZsdWpvKA0KICAgIGV2YWx1YWNpb25lc19pbmljaWFsZXMsDQogICAgIkJhc2UgaW5pY2lhbCINCiAgKSwNCiAgcmVzdW1pcl9ldGFwYV9mbHVqbygNCiAgICBldmFsdWFjaW9uZXNfc2luX3JlbGVzaW9uLA0KICAgICJMdWVnbyBkZSBleGNsdWlyIGp1Z2Fkb3JlcyBjb24gcmVsZXNpw7NuIg0KICApLA0KICByZXN1bWlyX2V0YXBhX2ZsdWpvKA0KICAgIGV2YWx1YWNpb25lc19jb25fcG9wLA0KICAgICJMdWVnbyBkZSBleGNsdWlyIGV2YWx1YWNpb25lcyBzaW4gbW9tZW50byBQT1AiDQogICksDQogIHJlc3VtaXJfZXRhcGFfZmx1am8oDQogICAgZXZhbHVhY2lvbmVzX2hhc3RhXzE4bSwNCiAgICAiTHVlZ28gZGUgZXhjbHVpciBldmFsdWFjaW9uZXMgPjE4IG1lc2VzIg0KICApLA0KICByZXN1bWlyX2V0YXBhX2ZsdWpvKA0KICAgIGV2YWx1YWNpb25lc19hbmFsaXNpcywNCiAgICAiQ29ob3J0ZSBhbmFsw610aWNhIGZpbmFsIg0KICApDQopICU+JQ0KICBtdXRhdGUoDQogICAganVnYWRvcmVzX2V4Y2x1aWRvc19kZXNkZV9ldGFwYV9wcmV2aWEgPSBsYWcobl9qdWdhZG9yZXMpIC0gbl9qdWdhZG9yZXMsDQogICAgZXZhbHVhY2lvbmVzX2V4Y2x1aWRhc19kZXNkZV9ldGFwYV9wcmV2aWEgPSBsYWcobl9ldmFsdWFjaW9uZXMpIC0gbl9ldmFsdWFjaW9uZXMNCiAgKQ0KDQpmbHVqb19leGNsdXNpb25lcw0KDQoNCiMgRGV0YWxsZSBkZSBleGNsdXNpb25lcyBwb3IgbW90aXZvDQpkZXRhbGxlX2V4Y2x1c2lvbmVzIDwtIGJpbmRfcm93cygNCiAgDQogIGV2YWx1YWNpb25lc19pbmljaWFsZXMgJT4lDQogICAgZmlsdGVyKGlkICVpbiUgaWRzX3JlbGVzaW9uX3NpKSAlPiUNCiAgICBzdW1tYXJpc2UoDQogICAgICBtb3Rpdm8gPSAiSnVnYWRvcmVzIGNvbiByZWxlc2nDs24iLA0KICAgICAgbl9qdWdhZG9yZXNfZXhjbHVpZG9zID0gbl9kaXN0aW5jdChpZCksDQogICAgICBuX2V2YWx1YWNpb25lc19leGNsdWlkYXMgPSBuKCkNCiAgICApLA0KICANCiAgZXZhbHVhY2lvbmVzX3Npbl9yZWxlc2lvbiAlPiUNCiAgICBmaWx0ZXIoaXMubmEobW9tZW50b19wb3BfbWVzZXMpKSAlPiUNCiAgICBzdW1tYXJpc2UoDQogICAgICBtb3Rpdm8gPSAiRXZhbHVhY2lvbmVzIHNpbiBtb21lbnRvIFBPUCBkaXNwb25pYmxlIiwNCiAgICAgIG5fanVnYWRvcmVzX2V4Y2x1aWRvcyA9IG5fZGlzdGluY3QoaWQpLA0KICAgICAgbl9ldmFsdWFjaW9uZXNfZXhjbHVpZGFzID0gbigpDQogICAgKSwNCiAgDQogIGV2YWx1YWNpb25lc19jb25fcG9wICU+JQ0KICAgIGZpbHRlcihtb21lbnRvX3BvcF9tZXNlcyA+IDE4KSAlPiUNCiAgICBzdW1tYXJpc2UoDQogICAgICBtb3Rpdm8gPSAiRXZhbHVhY2lvbmVzIHJlYWxpemFkYXMgYSA+MTggbWVzZXMiLA0KICAgICAgbl9qdWdhZG9yZXNfZXhjbHVpZG9zID0gbl9kaXN0aW5jdChpZCksDQogICAgICBuX2V2YWx1YWNpb25lc19leGNsdWlkYXMgPSBuKCkNCiAgICApLA0KICANCiAgZXZhbHVhY2lvbmVzX2hhc3RhXzE4bSAlPiUNCiAgICBtdXRhdGUoDQogICAgICBncnVwb19jb21wZXRlbmNpYSA9IGNhc2Vfd2hlbigNCiAgICAgICAgbml2ZWxfZGVfY29tcGV0ZW5jaWEgPT0gIlByaW1lcmEgZGl2aXNpw7NuIiB+ICJQcmltZXJhIiwNCiAgICAgICAgbml2ZWxfZGVfY29tcGV0ZW5jaWEgPT0gIlNlZ3VuZGEgZGl2aXNpw7NuIiB+ICJTZWd1bmRhIiwNCiAgICAgICAgbml2ZWxfZGVfY29tcGV0ZW5jaWEgJWluJSBjKA0KICAgICAgICAgICJUZXJjZXJhIERpdmlzacOzbiIsDQogICAgICAgICAgIkFtYXRldXIiDQogICAgICAgICkgfiAiVGVyY2VyYSArIEFtYXRldXIiLA0KICAgICAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXw0KICAgICAgKQ0KICAgICkgJT4lDQogICAgZmlsdGVyKGlzLm5hKGdydXBvX2NvbXBldGVuY2lhKSkgJT4lDQogICAgc3VtbWFyaXNlKA0KICAgICAgbW90aXZvID0gIkV2YWx1YWNpb25lcyBzaW4gZ3J1cG8gY29tcGV0aXRpdm8gY2xhc2lmaWNhYmxlIiwNCiAgICAgIG5fanVnYWRvcmVzX2V4Y2x1aWRvcyA9IG5fZGlzdGluY3QoaWQpLA0KICAgICAgbl9ldmFsdWFjaW9uZXNfZXhjbHVpZGFzID0gbigpDQogICAgKQ0KKQ0KDQpkZXRhbGxlX2V4Y2x1c2lvbmVzDQpgYGANCg0KIyMgSW50ZXJwcmV0YWNpw7NuIGRlbCBmbHVqbw0KDQpTZSBwYXJ0acOzIGRlICoqYHIgZmx1am9fZXhjbHVzaW9uZXMkbl9qdWdhZG9yZXNbMV1gIGp1Z2Fkb3JlcyoqIHkgKipgciBmbHVqb19leGNsdXNpb25lcyRuX2V2YWx1YWNpb25lc1sxXWAgZXZhbHVhY2lvbmVzKiogYSBuaXZlbCBkZSBzZXNpw7NuLg0KDQpEdXJhbnRlIGxhIGRlcHVyYWNpw7NuOg0KDQotICAgc2UgZXhjbHV5ZXJvbiAqKmByIGRldGFsbGVfZXhjbHVzaW9uZXMkbl9qdWdhZG9yZXNfZXhjbHVpZG9zW2RldGFsbGVfZXhjbHVzaW9uZXMkbW90aXZvID09ICJKdWdhZG9yZXMgY29uIHJlbGVzacOzbiJdYCBqdWdhZG9yZXMqKiBjb24gcmVsZXNpw7NuLCBjb3JyZXNwb25kaWVudGVzIGEgKipgciBkZXRhbGxlX2V4Y2x1c2lvbmVzJG5fZXZhbHVhY2lvbmVzX2V4Y2x1aWRhc1tkZXRhbGxlX2V4Y2x1c2lvbmVzJG1vdGl2byA9PSAiSnVnYWRvcmVzIGNvbiByZWxlc2nDs24iXWAgZXZhbHVhY2lvbmVzKio7DQotICAgc2UgZXhjbHV5ZXJvbiAqKmByIGRldGFsbGVfZXhjbHVzaW9uZXMkbl9ldmFsdWFjaW9uZXNfZXhjbHVpZGFzW2RldGFsbGVfZXhjbHVzaW9uZXMkbW90aXZvID09ICJFdmFsdWFjaW9uZXMgc2luIG1vbWVudG8gUE9QIGRpc3BvbmlibGUiXWAgZXZhbHVhY2lvbmVzKiogc2luIG1vbWVudG8gcG9zdG9wZXJhdG9yaW8gZGlzcG9uaWJsZTsNCi0gICBzZSBleGNsdXllcm9uICoqYHIgZGV0YWxsZV9leGNsdXNpb25lcyRuX2V2YWx1YWNpb25lc19leGNsdWlkYXNbZGV0YWxsZV9leGNsdXNpb25lcyRtb3Rpdm8gPT0gIkV2YWx1YWNpb25lcyByZWFsaXphZGFzIGEgPjE4IG1lc2VzIl1gIGV2YWx1YWNpb25lcyoqIHJlYWxpemFkYXMgYSBtw6FzIGRlIDE4IG1lc2VzIGRlIGxhIGNpcnVnw61hOw0KLSAgIHNlIGV4Y2x1eWVyb24gKipgciBkZXRhbGxlX2V4Y2x1c2lvbmVzJG5fZXZhbHVhY2lvbmVzX2V4Y2x1aWRhc1tkZXRhbGxlX2V4Y2x1c2lvbmVzJG1vdGl2byA9PSAiRXZhbHVhY2lvbmVzIHNpbiBncnVwbyBjb21wZXRpdGl2byBjbGFzaWZpY2FibGUiXWAgZXZhbHVhY2lvbmVzKiogc2luIG5pdmVsIGNvbXBldGl0aXZvIGNsYXNpZmljYWJsZS4NCg0KTGEgY29ob3J0ZSBhbmFsw610aWNhIGZpbmFsIHF1ZWTDsyBjb25mb3JtYWRhIHBvciAqKmByIGZsdWpvX2V4Y2x1c2lvbmVzJG5fanVnYWRvcmVzW2ZsdWpvX2V4Y2x1c2lvbmVzJGV0YXBhID09ICJDb2hvcnRlIGFuYWzDrXRpY2EgZmluYWwiXWAganVnYWRvcmVzKiogeSAqKmByIGZsdWpvX2V4Y2x1c2lvbmVzJG5fZXZhbHVhY2lvbmVzW2ZsdWpvX2V4Y2x1c2lvbmVzJGV0YXBhID09ICJDb2hvcnRlIGFuYWzDrXRpY2EgZmluYWwiXWAgZXZhbHVhY2lvbmVzKiouDQoNCkVzdGEgaW5mb3JtYWNpw7NuIHB1ZWRlIHV0aWxpemFyc2UgZGlyZWN0YW1lbnRlIHBhcmEgY29uc3RydWlyIGVsIGRpYWdyYW1hIGRlIGZsdWpvIGRlIHNlbGVjY2nDs24gZGUgbGEgbXVlc3RyYS4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgMTMuIFJlY29uc3RydWNjacOzbiBkZSBsYSBiYXNlIGFuYWzDrXRpY2EgY29uIGxhcyB0cmVzIHZlbG9jaWRhZGVzDQoNClVuYSB2ZXogZGVmaW5pZGEgbGEgY29ob3J0ZSBkZSBldmFsdWFjaW9uZXMgZWxlZ2libGVzLCBzZSByZWN1cGVyYW4gbGFzIHRyZXMgZmlsYXMgY29ycmVzcG9uZGllbnRlcyBhIGxhcyB2ZWxvY2lkYWRlcyBpc29jaW7DqXRpY2FzLg0KDQpgYGB7ciBkYXRvc19hbmFsaXNpc19maW5hbH0NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDEyKSBCQVNFIEFOQUzDjVRJQ0EgRklOQUwgQ09OIExBUyBUUkVTIFZFTE9DSURBREVTDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KDQpkYXRvc19hbmFsaXNpcyA8LSBkYXRvc19pc28gJT4lDQogIGlubmVyX2pvaW4oDQogICAgZXZhbHVhY2lvbmVzX2FuYWxpc2lzICU+JQ0KICAgICAgc2VsZWN0KA0KICAgICAgICBpZCwNCiAgICAgICAgc2VzaW9uLA0KICAgICAgICBncnVwb19jb21wZXRlbmNpYQ0KICAgICAgKSwNCiAgICBieSA9IGMoImlkIiwgInNlc2lvbiIpDQogICkNCg0KcmVzdW1lbl9tdWVzdHJhX2FuYWxpdGljYSA8LSB0aWJibGU6OnRpYmJsZSgNCiAgbl9qdWdhZG9yZXMgPSBuX2Rpc3RpbmN0KGV2YWx1YWNpb25lc19hbmFsaXNpcyRpZCksDQogIG5fZXZhbHVhY2lvbmVzID0gbnJvdyhldmFsdWFjaW9uZXNfYW5hbGlzaXMpLA0KICBuX2ZpbGFzX2lzb2NpbmV0aWNhcyA9IG5yb3coZGF0b3NfYW5hbGlzaXMpDQopDQoNCnJlc3VtZW5fbXVlc3RyYV9hbmFsaXRpY2ENCmBgYA0KDQojIyBJbnRlcnByZXRhY2nDs24gZGUgbGEgbXVlc3RyYSBmaW5hbA0KDQpMYSBjb2hvcnRlIGFuYWzDrXRpY2EgaW5jbHV5w7M6DQoNCi0gICAqKmByIHJlc3VtZW5fbXVlc3RyYV9hbmFsaXRpY2Ekbl9qdWdhZG9yZXNgIGp1Z2Fkb3JlcyoqOw0KLSAgICoqYHIgcmVzdW1lbl9tdWVzdHJhX2FuYWxpdGljYSRuX2V2YWx1YWNpb25lc2AgZXZhbHVhY2lvbmVzIGNsw61uaWNhcyDDum5pY2FzKio7DQotICAgKipgciByZXN1bWVuX211ZXN0cmFfYW5hbGl0aWNhJG5fZmlsYXNfaXNvY2luZXRpY2FzYCBmaWxhcyBkZSBkYXRvcyBpc29jaW7DqXRpY29zKiosIGNvcnJlc3BvbmRpZW50ZXMgYSBsYXMgZGlzdGludGFzIHZlbG9jaWRhZGVzIGRpc3BvbmlibGVzLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyAxNC4gRGlzdHJpYnVjacOzbiBkZSBqdWdhZG9yZXMgeSBldmFsdWFjaW9uZXMgcG9yIG5pdmVsIGNvbXBldGl0aXZvDQoNCmBgYHtyIG11ZXN0cmFfcG9yX2dydXBvfQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMTMpIEpVR0FET1JFUyBZIEVWQUxVQUNJT05FUyBQT1IgR1JVUE8gQ09NUEVUSVRJVk8NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNCmNvbnRlb19tdWVzdHJhX3Bvcl9ncnVwbyA8LSBldmFsdWFjaW9uZXNfYW5hbGlzaXMgJT4lDQogIGdyb3VwX2J5KGdydXBvX2NvbXBldGVuY2lhKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIG5fanVnYWRvcmVzID0gbl9kaXN0aW5jdChpZCksDQogICAgbl9ldmFsdWFjaW9uZXMgPSBuKCksDQogICAgLmdyb3VwcyA9ICJkcm9wIg0KICApDQoNCmNvbnRlb19tdWVzdHJhX3Bvcl9ncnVwbw0KDQoNCnRleHRvX2dydXBvcyA8LSBjb250ZW9fbXVlc3RyYV9wb3JfZ3J1cG8gJT4lDQogIG11dGF0ZSgNCiAgICB0ZXh0byA9IHBhc3RlMCgNCiAgICAgIGdydXBvX2NvbXBldGVuY2lhLA0KICAgICAgIjogIiwNCiAgICAgIG5fanVnYWRvcmVzLA0KICAgICAgIiBqdWdhZG9yZXMgeSAiLA0KICAgICAgbl9ldmFsdWFjaW9uZXMsDQogICAgICAiIGV2YWx1YWNpb25lcyINCiAgICApDQogICkgJT4lDQogIHB1bGwodGV4dG8pICU+JQ0KICBwYXN0ZShjb2xsYXBzZSA9ICI7ICIpDQoNCnRleHRvX2dydXBvcw0KYGBgDQoNCiMjIEludGVycHJldGFjacOzbg0KDQpMYSBkaXN0cmlidWNpw7NuIHBvciBuaXZlbCBjb21wZXRpdGl2byBmdWUgbGEgc2lndWllbnRlOg0KDQoqKmByIHRleHRvX2dydXBvc2AuKioNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgMTUuIERpc3RyaWJ1Y2nDs24gZGUgZXZhbHVhY2lvbmVzIHBvciBncnVwbyBjb21wZXRpdGl2byB5IG1vbWVudG8gcG9zdG9wZXJhdG9yaW8NCg0KYGBge3IgbXVlc3RyYV9wb3JfZ3J1cG9feV90aWVtcG99DQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAxNCkgSlVHQURPUkVTIFkgRVZBTFVBQ0lPTkVTIFBPUiBHUlVQTyBZIFRJRU1QTw0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KY29udGVvX211ZXN0cmFfcG9yX2dydXBvX3lfdGllbXBvIDwtIGV2YWx1YWNpb25lc19hbmFsaXNpcyAlPiUNCiAgZ3JvdXBfYnkoDQogICAgZ3J1cG9fY29tcGV0ZW5jaWEsDQogICAgY2F0ZWdvcmlhX21vbWVudG9fcG9wX2Rlcml2YWRhDQogICkgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBuX2p1Z2Fkb3JlcyA9IG5fZGlzdGluY3QoaWQpLA0KICAgIG5fZXZhbHVhY2lvbmVzID0gbigpLA0KICAgIC5ncm91cHMgPSAiZHJvcCINCiAgKSAlPiUNCiAgYXJyYW5nZSgNCiAgICBncnVwb19jb21wZXRlbmNpYSwNCiAgICBjYXRlZ29yaWFfbW9tZW50b19wb3BfZGVyaXZhZGENCiAgKQ0KDQpjb250ZW9fbXVlc3RyYV9wb3JfZ3J1cG9feV90aWVtcG8NCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyAxNi4gRnVuY2lvbmVzIGRlIGZvcm1hdG8gcGFyYSBsYXMgdGFibGFzIGRlc2NyaXB0aXZhcw0KDQpgYGB7ciBmdW5jaW9uZXNfZm9ybWF0b30NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDE1KSBGVU5DSU9ORVMgREUgRk9STUFUTw0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KZm9ybWF0ZWFyX251bWVybyA8LSBmdW5jdGlvbih4LCBkaWdpdG9zID0gMil7DQogIA0KICBpZmVsc2UoDQogICAgaXMubmEoeCksDQogICAgIk5BIiwNCiAgICBmb3JtYXRDKA0KICAgICAgeCwNCiAgICAgIGZvcm1hdCA9ICJmIiwNCiAgICAgIGRpZ2l0cyA9IGRpZ2l0b3MsDQogICAgICBkZWNpbWFsLm1hcmsgPSAiLCINCiAgICApDQogICkNCn0NCg0KDQpmb3JtYXRlYXJfbWVkaWFfZGUgPC0gZnVuY3Rpb24obWVkaWEsIGRlLCBkaWdpdG9zID0gMil7DQogIA0KICBwYXN0ZTAoDQogICAgZm9ybWF0ZWFyX251bWVybyhtZWRpYSwgZGlnaXRvcyksDQogICAgIiDCsSAiLA0KICAgIGZvcm1hdGVhcl9udW1lcm8oZGUsIGRpZ2l0b3MpDQogICkNCn0NCg0KDQpmb3JtYXRlYXJfbWVkaWFuYV9pcXIgPC0gZnVuY3Rpb24obWVkaWFuYSwgcDI1LCBwNzUsIGRpZ2l0b3MgPSAyKXsNCiAgDQogIHBhc3RlMCgNCiAgICBmb3JtYXRlYXJfbnVtZXJvKG1lZGlhbmEsIGRpZ2l0b3MpLA0KICAgICIgWyIsDQogICAgZm9ybWF0ZWFyX251bWVybyhwMjUsIGRpZ2l0b3MpLA0KICAgICI7ICIsDQogICAgZm9ybWF0ZWFyX251bWVybyhwNzUsIGRpZ2l0b3MpLA0KICAgICJdIg0KICApDQp9DQoNCg0KZm9ybWF0ZWFyX25fcG9yY2VudGFqZSA8LSBmdW5jdGlvbihuLCBwb3JjZW50YWplLCBkaWdpdG9zID0gMSl7DQogIA0KICBwYXN0ZTAoDQogICAgbiwNCiAgICAiICgiLA0KICAgIGZvcm1hdGVhcl9udW1lcm8ocG9yY2VudGFqZSwgZGlnaXRvcyksDQogICAgIiUpIg0KICApDQp9DQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgMTcuIFRhYmxhIDE6IGNhcmFjdGVyaXphY2nDs24gZ2VuZXJhbCBkZSBsYSBjb2hvcnRlDQoNCkxhIFRhYmxhIDEgc2UgY29uc3RydXnDsyBhIG5pdmVsIGRlICoqZXZhbHVhY2nDs24gw7puaWNhKiouIExhcyB2YXJpYWJsZXMgbnVtw6lyaWNhcyBzZSByZXN1bWVuIGRlIGZvcm1hIGFtcGxpYSBwYXJhIHBlcm1pdGlyIGRlY2lkaXIgcG9zdGVyaW9ybWVudGUgc2kgbGEgdmVyc2nDs24gZmluYWwgZGVsIG1hbnVzY3JpdG8gcmVwb3J0YXLDoToNCg0KLSAgIG1lZGlhIHkgZGVzdsOtbyBlc3TDoW5kYXIsIG8NCi0gICBtZWRpYW5hIHkgUDI14oCTUDc1Lg0KDQpFbiBlc3RhIGV0YXBhIHNlIHByZXNlbnRhbiBhbWJhcyBmb3JtYXMgZGUgcmVzdW1lbi4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDE3LjEgVmFyaWFibGVzIGNvbnNpZGVyYWRhcyBlbiBsYSBUYWJsYSAxDQoNCmBgYHtyIHZhcmlhYmxlc190YWJsYTF9DQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAxNikgVkFSSUFCTEVTIFBBUkEgVEFCTEEgMQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KdmFyc190YWJsYTFfbnVtZXJpY2FzIDwtIGMoDQogICJlZGFkX2V2YWx1YWNpb24iLA0KICAiYWx0dXJhIiwNCiAgInBlc28iLA0KICAibW9tZW50b19wb3BfbWVzZXMiDQopDQoNCmV0aXF1ZXRhc190YWJsYTFfbnVtZXJpY2FzIDwtIGMoDQogICJFZGFkIGFsIG1vbWVudG8gZGUgbGEgZXZhbHVhY2nDs24sIGHDsW9zIiwNCiAgIkFsdHVyYSwgY20iLA0KICAiUGVzbywga2ciLA0KICAiTW9tZW50byBwb3N0cXVpcsO6cmdpY28sIG1lc2VzIg0KKQ0KDQoNCnZhcnNfdGFibGExX2NhdGVnb3JpY2FzIDwtIGMoDQogICJpbXBsaWNhZG8iLA0KICAiY2F0ZWdvcmlhX21vbWVudG9fcG9wX2Rlcml2YWRhIiwNCiAgImluamVydG8iLA0KICAibGVtYWlyZV9yZWZ1ZXJ6b19sYXRlcmFsIg0KKQ0KDQpldGlxdWV0YXNfdGFibGExX2NhdGVnb3JpY2FzIDwtIGMoDQogICJNaWVtYnJvIGltcGxpY2FkbyIsDQogICJNb21lbnRvIGRlIGV2YWx1YWNpw7NuIiwNCiAgIlRpcG8gZGUgaW5qZXJ0byIsDQogICJSZWZ1ZXJ6byBsYXRlcmFsIHRpcG8gTGVtYWlyZSINCikNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMTcuMiBEaXNwb25pYmlsaWRhZCBkZSBkYXRvcyBkZSBUYWJsYSAxDQoNCmBgYHtyIGRpc3BvbmliaWxpZGFkX3RhYmxhMX0NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDE3KSBESVNQT05JQklMSURBRCBERSBEQVRPUyBERSBUQUJMQSAxDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KDQpuX2Rpc3BvbmlibGVzX3RhYmxhMV90b3RhbCA8LSBiaW5kX3Jvd3MoDQogIA0KICBwdXJycjo6bWFwMl9kZnIoDQogICAgdmFyc190YWJsYTFfbnVtZXJpY2FzLA0KICAgIGV0aXF1ZXRhc190YWJsYTFfbnVtZXJpY2FzLA0KICAgIGZ1bmN0aW9uKHYsIGV0aXF1ZXRhKXsNCiAgICAgIA0KICAgICAgZXZhbHVhY2lvbmVzX2FuYWxpc2lzICU+JQ0KICAgICAgICBmaWx0ZXIoIWlzLm5hKC5kYXRhW1t2XV0pKSAlPiUNCiAgICAgICAgc3VtbWFyaXNlKA0KICAgICAgICAgIHZhcmlhYmxlID0gZXRpcXVldGEsDQogICAgICAgICAgbl9ldmFsdWFjaW9uZXNfZGlzcG9uaWJsZXMgPSBuKCksDQogICAgICAgICAgbl9qdWdhZG9yZXNfZGlzcG9uaWJsZXMgPSBuX2Rpc3RpbmN0KGlkKQ0KICAgICAgICApDQogICAgfQ0KICApLA0KICANCiAgcHVycnI6Om1hcDJfZGZyKA0KICAgIHZhcnNfdGFibGExX2NhdGVnb3JpY2FzLA0KICAgIGV0aXF1ZXRhc190YWJsYTFfY2F0ZWdvcmljYXMsDQogICAgZnVuY3Rpb24odiwgZXRpcXVldGEpew0KICAgICAgDQogICAgICBldmFsdWFjaW9uZXNfYW5hbGlzaXMgJT4lDQogICAgICAgIGZpbHRlcighaXMubmEoLmRhdGFbW3ZdXSkpICU+JQ0KICAgICAgICBzdW1tYXJpc2UoDQogICAgICAgICAgdmFyaWFibGUgPSBldGlxdWV0YSwNCiAgICAgICAgICBuX2V2YWx1YWNpb25lc19kaXNwb25pYmxlcyA9IG4oKSwNCiAgICAgICAgICBuX2p1Z2Fkb3Jlc19kaXNwb25pYmxlcyA9IG5fZGlzdGluY3QoaWQpDQogICAgICAgICkNCiAgICB9DQogICkNCikNCg0Kbl9kaXNwb25pYmxlc190YWJsYTFfdG90YWwNCg0KDQpuX2Rpc3BvbmlibGVzX3RhYmxhMV9wb3JfZ3J1cG8gPC0gYmluZF9yb3dzKA0KICANCiAgcHVycnI6Om1hcDJfZGZyKA0KICAgIHZhcnNfdGFibGExX251bWVyaWNhcywNCiAgICBldGlxdWV0YXNfdGFibGExX251bWVyaWNhcywNCiAgICBmdW5jdGlvbih2LCBldGlxdWV0YSl7DQogICAgICANCiAgICAgIGV2YWx1YWNpb25lc19hbmFsaXNpcyAlPiUNCiAgICAgICAgZmlsdGVyKCFpcy5uYSguZGF0YVtbdl1dKSkgJT4lDQogICAgICAgIGdyb3VwX2J5KGdydXBvX2NvbXBldGVuY2lhKSAlPiUNCiAgICAgICAgc3VtbWFyaXNlKA0KICAgICAgICAgIHZhcmlhYmxlID0gZXRpcXVldGEsDQogICAgICAgICAgbl9ldmFsdWFjaW9uZXNfZGlzcG9uaWJsZXMgPSBuKCksDQogICAgICAgICAgbl9qdWdhZG9yZXNfZGlzcG9uaWJsZXMgPSBuX2Rpc3RpbmN0KGlkKSwNCiAgICAgICAgICAuZ3JvdXBzID0gImRyb3AiDQogICAgICAgICkNCiAgICB9DQogICksDQogIA0KICBwdXJycjo6bWFwMl9kZnIoDQogICAgdmFyc190YWJsYTFfY2F0ZWdvcmljYXMsDQogICAgZXRpcXVldGFzX3RhYmxhMV9jYXRlZ29yaWNhcywNCiAgICBmdW5jdGlvbih2LCBldGlxdWV0YSl7DQogICAgICANCiAgICAgIGV2YWx1YWNpb25lc19hbmFsaXNpcyAlPiUNCiAgICAgICAgZmlsdGVyKCFpcy5uYSguZGF0YVtbdl1dKSkgJT4lDQogICAgICAgIGdyb3VwX2J5KGdydXBvX2NvbXBldGVuY2lhKSAlPiUNCiAgICAgICAgc3VtbWFyaXNlKA0KICAgICAgICAgIHZhcmlhYmxlID0gZXRpcXVldGEsDQogICAgICAgICAgbl9ldmFsdWFjaW9uZXNfZGlzcG9uaWJsZXMgPSBuKCksDQogICAgICAgICAgbl9qdWdhZG9yZXNfZGlzcG9uaWJsZXMgPSBuX2Rpc3RpbmN0KGlkKSwNCiAgICAgICAgICAuZ3JvdXBzID0gImRyb3AiDQogICAgICAgICkNCiAgICB9DQogICkNCikgJT4lDQogIGFycmFuZ2UoDQogICAgdmFyaWFibGUsDQogICAgZ3J1cG9fY29tcGV0ZW5jaWENCiAgKQ0KDQpuX2Rpc3BvbmlibGVzX3RhYmxhMV9wb3JfZ3J1cG8NCmBgYA0KDQojIyBJbnRlcnByZXRhY2nDs24NCg0KTGEgdGFibGEgZGUgZGlzcG9uaWJpbGlkYWQgcGVybWl0ZSBpZGVudGlmaWNhciBzb2JyZSBjdcOhbnRhcyBldmFsdWFjaW9uZXMgeSBqdWdhZG9yZXMgc2UgY2FsY3Vsw7MgY2FkYSB2YXJpYWJsZS5cDQpQb3IgZWplbXBsbywgc2kgdW5hIHZhcmlhYmxlIHRpZW5lIDcwIGV2YWx1YWNpb25lcyBkaXNwb25pYmxlcyB5IDQ2IGp1Z2Fkb3JlcyBkaXNwb25pYmxlcywgc2lnbmlmaWNhIHF1ZSBlc2EgaW5mb3JtYWNpw7NuIGVzdHV2byByZWdpc3RyYWRhIGVuIHRvZGEgbGEgY29ob3J0ZSBmaW5hbC4gU2kgcHJlc2VudGEgdW4gbsO6bWVybyBtZW5vciwgZXhpc3RlbiBkYXRvcyBmYWx0YW50ZXMgcGFyYSBlc2EgdmFyaWFibGUuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAxNy4zIFRhYmxhIDE6IHZhcmlhYmxlcyBudW3DqXJpY2FzDQoNCmBgYHtyIHRhYmxhMV9udW1lcmljYXN9DQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAxOCkgVEFCTEEgMSAtIFZBUklBQkxFUyBOVU3DiVJJQ0FTDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KDQp0YWJsYTFfbnVtZXJpY2FzX3Jlc3VtZW4gPC0gcHVycnI6Om1hcDJfZGZyKA0KICB2YXJzX3RhYmxhMV9udW1lcmljYXMsDQogIGV0aXF1ZXRhc190YWJsYTFfbnVtZXJpY2FzLA0KICBmdW5jdGlvbih2LCBldGlxdWV0YSl7DQogICAgDQogICAgdG1wIDwtIGV2YWx1YWNpb25lc19hbmFsaXNpcyAlPiUNCiAgICAgIHRyYW5zbXV0ZSgNCiAgICAgICAgaWQsDQogICAgICAgIGdydXBvX2NvbXBldGVuY2lhLA0KICAgICAgICB2YWxvciA9IC5kYXRhW1t2XV0NCiAgICAgICkgJT4lDQogICAgICBmaWx0ZXIoDQogICAgICAgICFpcy5uYSh2YWxvciksDQogICAgICAgICFpcy5uYShncnVwb19jb21wZXRlbmNpYSkNCiAgICAgICkNCiAgICANCiAgICANCiAgICByZXN1bWVuX3Bvcl9ncnVwbyA8LSB0bXAgJT4lDQogICAgICBncm91cF9ieShncnVwb19jb21wZXRlbmNpYSkgJT4lDQogICAgICBzdW1tYXJpc2UoDQogICAgICAgIHZhcmlhYmxlID0gZXRpcXVldGEsDQogICAgICAgIG5fZXZhbHVhY2lvbmVzX2Rpc3BvbmlibGVzID0gbigpLA0KICAgICAgICBuX2p1Z2Fkb3Jlc19kaXNwb25pYmxlcyA9IG5fZGlzdGluY3QoaWQpLA0KICAgICAgICBtZWRpYSA9IG1lYW4odmFsb3IsIG5hLnJtID0gVFJVRSksDQogICAgICAgIGRlID0gc2QodmFsb3IsIG5hLnJtID0gVFJVRSksDQogICAgICAgIG1lZGlhbmEgPSBtZWRpYW4odmFsb3IsIG5hLnJtID0gVFJVRSksDQogICAgICAgIHAyNSA9IHF1YW50aWxlKHZhbG9yLCAwLjI1LCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICBwNzUgPSBxdWFudGlsZSh2YWxvciwgMC43NSwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgaXFyID0gSVFSKHZhbG9yLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICBtaW5pbW8gPSBtaW4odmFsb3IsIG5hLnJtID0gVFJVRSksDQogICAgICAgIG1heGltbyA9IG1heCh2YWxvciwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgLmdyb3VwcyA9ICJkcm9wIg0KICAgICAgKQ0KICAgIA0KICAgIA0KICAgIHJlc3VtZW5fdG90YWwgPC0gdG1wICU+JQ0KICAgICAgc3VtbWFyaXNlKA0KICAgICAgICBncnVwb19jb21wZXRlbmNpYSA9ICJUb3RhbCIsDQogICAgICAgIHZhcmlhYmxlID0gZXRpcXVldGEsDQogICAgICAgIG5fZXZhbHVhY2lvbmVzX2Rpc3BvbmlibGVzID0gbigpLA0KICAgICAgICBuX2p1Z2Fkb3Jlc19kaXNwb25pYmxlcyA9IG5fZGlzdGluY3QoaWQpLA0KICAgICAgICBtZWRpYSA9IG1lYW4odmFsb3IsIG5hLnJtID0gVFJVRSksDQogICAgICAgIGRlID0gc2QodmFsb3IsIG5hLnJtID0gVFJVRSksDQogICAgICAgIG1lZGlhbmEgPSBtZWRpYW4odmFsb3IsIG5hLnJtID0gVFJVRSksDQogICAgICAgIHAyNSA9IHF1YW50aWxlKHZhbG9yLCAwLjI1LCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICBwNzUgPSBxdWFudGlsZSh2YWxvciwgMC43NSwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgaXFyID0gSVFSKHZhbG9yLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICBtaW5pbW8gPSBtaW4odmFsb3IsIG5hLnJtID0gVFJVRSksDQogICAgICAgIG1heGltbyA9IG1heCh2YWxvciwgbmEucm0gPSBUUlVFKQ0KICAgICAgKQ0KICAgIA0KICAgIA0KICAgIGJpbmRfcm93cygNCiAgICAgIHJlc3VtZW5fcG9yX2dydXBvICU+JQ0KICAgICAgICBtdXRhdGUoZ3J1cG9fY29tcGV0ZW5jaWEgPSBhcy5jaGFyYWN0ZXIoZ3J1cG9fY29tcGV0ZW5jaWEpKSwNCiAgICAgIHJlc3VtZW5fdG90YWwNCiAgICApDQogIH0NCikgJT4lDQogIHJlbG9jYXRlKA0KICAgIHZhcmlhYmxlLA0KICAgIGdydXBvX2NvbXBldGVuY2lhLA0KICAgIG5fZXZhbHVhY2lvbmVzX2Rpc3BvbmlibGVzLA0KICAgIG5fanVnYWRvcmVzX2Rpc3BvbmlibGVzDQogICkNCg0KdGFibGExX251bWVyaWNhc19yZXN1bWVuDQoNCg0KdGFibGExX251bWVyaWNhc19mb3JtYXRlYWRhIDwtIHRhYmxhMV9udW1lcmljYXNfcmVzdW1lbiAlPiUNCiAgbXV0YXRlKA0KICAgIG1lZGlhX2RlID0gZm9ybWF0ZWFyX21lZGlhX2RlKA0KICAgICAgbWVkaWEsDQogICAgICBkZSwNCiAgICAgIGRpZ2l0b3MgPSAyDQogICAgKSwNCiAgICBtZWRpYW5hX3AyNV9wNzUgPSBmb3JtYXRlYXJfbWVkaWFuYV9pcXIoDQogICAgICBtZWRpYW5hLA0KICAgICAgcDI1LA0KICAgICAgcDc1LA0KICAgICAgZGlnaXRvcyA9IDINCiAgICApDQogICkgJT4lDQogIHNlbGVjdCgNCiAgICB2YXJpYWJsZSwNCiAgICBncnVwb19jb21wZXRlbmNpYSwNCiAgICBuX2V2YWx1YWNpb25lc19kaXNwb25pYmxlcywNCiAgICBuX2p1Z2Fkb3Jlc19kaXNwb25pYmxlcywNCiAgICBtZWRpYV9kZSwNCiAgICBtZWRpYW5hX3AyNV9wNzUsDQogICAgaXFyLA0KICAgIG1pbmltbywNCiAgICBtYXhpbW8NCiAgKQ0KDQp0YWJsYTFfbnVtZXJpY2FzX2Zvcm1hdGVhZGENCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMTcuNCBUYWJsYSAxOiB2YXJpYWJsZXMgY2F0ZWfDs3JpY2FzDQoNCmBgYHtyIHRhYmxhMV9jYXRlZ29yaWNhc30NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDE5KSBUQUJMQSAxIC0gVkFSSUFCTEVTIENBVEVHw5NSSUNBUw0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KdGFibGExX2NhdGVnb3JpY2FzX3Jlc3VtZW4gPC0gcHVycnI6Om1hcDJfZGZyKA0KICB2YXJzX3RhYmxhMV9jYXRlZ29yaWNhcywNCiAgZXRpcXVldGFzX3RhYmxhMV9jYXRlZ29yaWNhcywNCiAgZnVuY3Rpb24odiwgZXRpcXVldGEpew0KICAgIA0KICAgIHRtcCA8LSBldmFsdWFjaW9uZXNfYW5hbGlzaXMgJT4lDQogICAgICB0cmFuc211dGUoDQogICAgICAgIGlkLA0KICAgICAgICBncnVwb19jb21wZXRlbmNpYSwNCiAgICAgICAgY2F0ZWdvcmlhID0gYXMuY2hhcmFjdGVyKC5kYXRhW1t2XV0pDQogICAgICApICU+JQ0KICAgICAgZmlsdGVyKA0KICAgICAgICAhaXMubmEoY2F0ZWdvcmlhKSwNCiAgICAgICAgIWlzLm5hKGdydXBvX2NvbXBldGVuY2lhKQ0KICAgICAgKQ0KICAgIA0KICAgIA0KICAgIGRlbm9taW5hZG9yZXNfZ3J1cG8gPC0gdG1wICU+JQ0KICAgICAgZ3JvdXBfYnkoZ3J1cG9fY29tcGV0ZW5jaWEpICU+JQ0KICAgICAgc3VtbWFyaXNlKA0KICAgICAgICBuX2V2YWx1YWNpb25lc19kaXNwb25pYmxlc192YXJpYWJsZSA9IG4oKSwNCiAgICAgICAgbl9qdWdhZG9yZXNfZGlzcG9uaWJsZXNfdmFyaWFibGUgPSBuX2Rpc3RpbmN0KGlkKSwNCiAgICAgICAgLmdyb3VwcyA9ICJkcm9wIg0KICAgICAgKQ0KICAgIA0KICAgIA0KICAgIHJlc3VtZW5fcG9yX2dydXBvIDwtIHRtcCAlPiUNCiAgICAgIGdyb3VwX2J5KA0KICAgICAgICBncnVwb19jb21wZXRlbmNpYSwNCiAgICAgICAgY2F0ZWdvcmlhDQogICAgICApICU+JQ0KICAgICAgc3VtbWFyaXNlKA0KICAgICAgICB2YXJpYWJsZSA9IGV0aXF1ZXRhLA0KICAgICAgICBuX2V2YWx1YWNpb25lc19jYXRlZ29yaWEgPSBuKCksDQogICAgICAgIG5fanVnYWRvcmVzX2NhdGVnb3JpYSA9IG5fZGlzdGluY3QoaWQpLA0KICAgICAgICAuZ3JvdXBzID0gImRyb3AiDQogICAgICApICU+JQ0KICAgICAgbGVmdF9qb2luKA0KICAgICAgICBkZW5vbWluYWRvcmVzX2dydXBvLA0KICAgICAgICBieSA9ICJncnVwb19jb21wZXRlbmNpYSINCiAgICAgICkgJT4lDQogICAgICBtdXRhdGUoDQogICAgICAgIHBvcmNlbnRhamVfZXZhbHVhY2lvbmVzID0gDQogICAgICAgICAgMTAwICogbl9ldmFsdWFjaW9uZXNfY2F0ZWdvcmlhIC8NCiAgICAgICAgICBuX2V2YWx1YWNpb25lc19kaXNwb25pYmxlc192YXJpYWJsZQ0KICAgICAgKQ0KICAgIA0KICAgIA0KICAgIGRlbm9taW5hZG9yX3RvdGFsIDwtIHRtcCAlPiUNCiAgICAgIHN1bW1hcmlzZSgNCiAgICAgICAgZ3J1cG9fY29tcGV0ZW5jaWEgPSAiVG90YWwiLA0KICAgICAgICBuX2V2YWx1YWNpb25lc19kaXNwb25pYmxlc192YXJpYWJsZSA9IG4oKSwNCiAgICAgICAgbl9qdWdhZG9yZXNfZGlzcG9uaWJsZXNfdmFyaWFibGUgPSBuX2Rpc3RpbmN0KGlkKQ0KICAgICAgKQ0KICAgIA0KICAgIA0KICAgIHJlc3VtZW5fdG90YWwgPC0gdG1wICU+JQ0KICAgICAgZ3JvdXBfYnkoY2F0ZWdvcmlhKSAlPiUNCiAgICAgIHN1bW1hcmlzZSgNCiAgICAgICAgZ3J1cG9fY29tcGV0ZW5jaWEgPSAiVG90YWwiLA0KICAgICAgICB2YXJpYWJsZSA9IGV0aXF1ZXRhLA0KICAgICAgICBuX2V2YWx1YWNpb25lc19jYXRlZ29yaWEgPSBuKCksDQogICAgICAgIG5fanVnYWRvcmVzX2NhdGVnb3JpYSA9IG5fZGlzdGluY3QoaWQpLA0KICAgICAgICAuZ3JvdXBzID0gImRyb3AiDQogICAgICApICU+JQ0KICAgICAgYmluZF9jb2xzKA0KICAgICAgICBkZW5vbWluYWRvcl90b3RhbCAlPiUNCiAgICAgICAgICBzZWxlY3QoDQogICAgICAgICAgICBuX2V2YWx1YWNpb25lc19kaXNwb25pYmxlc192YXJpYWJsZSwNCiAgICAgICAgICAgIG5fanVnYWRvcmVzX2Rpc3BvbmlibGVzX3ZhcmlhYmxlDQogICAgICAgICAgKQ0KICAgICAgKSAlPiUNCiAgICAgIG11dGF0ZSgNCiAgICAgICAgcG9yY2VudGFqZV9ldmFsdWFjaW9uZXMgPSANCiAgICAgICAgICAxMDAgKiBuX2V2YWx1YWNpb25lc19jYXRlZ29yaWEgLw0KICAgICAgICAgIG5fZXZhbHVhY2lvbmVzX2Rpc3BvbmlibGVzX3ZhcmlhYmxlDQogICAgICApDQogICAgDQogICAgDQogICAgYmluZF9yb3dzKA0KICAgICAgcmVzdW1lbl9wb3JfZ3J1cG8gJT4lDQogICAgICAgIG11dGF0ZShncnVwb19jb21wZXRlbmNpYSA9IGFzLmNoYXJhY3RlcihncnVwb19jb21wZXRlbmNpYSkpLA0KICAgICAgcmVzdW1lbl90b3RhbA0KICAgICkNCiAgfQ0KKSAlPiUNCiAgcmVsb2NhdGUoDQogICAgdmFyaWFibGUsDQogICAgY2F0ZWdvcmlhLA0KICAgIGdydXBvX2NvbXBldGVuY2lhLA0KICAgIG5fZXZhbHVhY2lvbmVzX2NhdGVnb3JpYSwNCiAgICBwb3JjZW50YWplX2V2YWx1YWNpb25lcywNCiAgICBuX2p1Z2Fkb3Jlc19jYXRlZ29yaWEsDQogICAgbl9ldmFsdWFjaW9uZXNfZGlzcG9uaWJsZXNfdmFyaWFibGUsDQogICAgbl9qdWdhZG9yZXNfZGlzcG9uaWJsZXNfdmFyaWFibGUNCiAgKQ0KDQp0YWJsYTFfY2F0ZWdvcmljYXNfcmVzdW1lbg0KDQoNCnRhYmxhMV9jYXRlZ29yaWNhc19mb3JtYXRlYWRhIDwtIHRhYmxhMV9jYXRlZ29yaWNhc19yZXN1bWVuICU+JQ0KICBtdXRhdGUoDQogICAgbl9wb3JjZW50YWplX2V2YWx1YWNpb25lcyA9IGZvcm1hdGVhcl9uX3BvcmNlbnRhamUoDQogICAgICBuX2V2YWx1YWNpb25lc19jYXRlZ29yaWEsDQogICAgICBwb3JjZW50YWplX2V2YWx1YWNpb25lcywNCiAgICAgIGRpZ2l0b3MgPSAxDQogICAgKQ0KICApICU+JQ0KICBzZWxlY3QoDQogICAgdmFyaWFibGUsDQogICAgY2F0ZWdvcmlhLA0KICAgIGdydXBvX2NvbXBldGVuY2lhLA0KICAgIG5fcG9yY2VudGFqZV9ldmFsdWFjaW9uZXMsDQogICAgbl9qdWdhZG9yZXNfY2F0ZWdvcmlhLA0KICAgIG5fZXZhbHVhY2lvbmVzX2Rpc3BvbmlibGVzX3ZhcmlhYmxlLA0KICAgIG5fanVnYWRvcmVzX2Rpc3BvbmlibGVzX3ZhcmlhYmxlDQogICkNCg0KdGFibGExX2NhdGVnb3JpY2FzX2Zvcm1hdGVhZGENCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyAxOC4gRXZhbHVhY2nDs24gZGUgbGEgZGlzdHJpYnVjacOzbiBkZSBsYXMgdmFyaWFibGVzIG51bcOpcmljYXMgZGUgVGFibGEgMQ0KDQpMYSBldmFsdWFjacOzbiBkZSBkaXN0cmlidWNpw7NuIHNlIHJlYWxpesOzIMO6bmljYW1lbnRlIHBhcmEgbGFzIHZhcmlhYmxlcyBudW3DqXJpY2FzIGRlIGxhIFRhYmxhIDEuXA0KRXN0ZSBwYXNvIHBlcm1pdGUgZGVjaWRpciwgZGUgY2FyYSBhIGxhIHZlcnNpw7NuIGZpbmFsIGRlbCBtYW51c2NyaXRvLCBzaSBjYWRhIHZhcmlhYmxlIGNvbnZpZW5lIHJlcG9ydGFybGEgY29tbzoNCg0KLSAgICoqbWVkaWEgKERFKSoqLCBvDQotICAgKiptZWRpYW5hIFtQMjXigJNQNzVdKiouDQoNCkxhIGRlY2lzacOzbiBubyBkZWJlIGJhc2Fyc2UgZW4gdW4gw7puaWNvIGluZGljYWRvciwgc2lubyBlbiBsYSBjb21iaW5hY2nDs24gZGU6DQoNCi0gICBmb3JtYSBkZSBsb3MgaGlzdG9ncmFtYXM7DQotICAgZ3LDoWZpY29zIFEtUTsNCi0gICBwcmVzZW5jaWEgZGUgdmFsb3JlcyBleHRyZW1vczsNCi0gICBhc2ltZXRyw61hOw0KLSAgIHRlc3QgZGUgU2hhcGlyby1XaWxrLg0KDQpgYGB7ciBkaXN0cmlidWNpb25fdGFibGExfQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMjApIEFOw4FMSVNJUyBERSBESVNUUklCVUNJw5NOIC0gVEFCTEEgMQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KYXNpbWV0cmlhX3BlYXJzb24gPC0gZnVuY3Rpb24oeCl7DQogIA0KICB4IDwtIHhbIWlzLm5hKHgpXQ0KICANCiAgaWYobGVuZ3RoKHgpIDwgMyB8fCBzZCh4KSA9PSAwKXsNCiAgICByZXR1cm4oTkFfcmVhbF8pDQogIH0NCiAgDQogIDMgKiAobWVhbih4KSAtIG1lZGlhbih4KSkgLyBzZCh4KQ0KfQ0KDQoNCmNvbnRhcl9vdXRsaWVyc19pcXIgPC0gZnVuY3Rpb24oeCl7DQogIA0KICB4IDwtIHhbIWlzLm5hKHgpXQ0KICANCiAgaWYobGVuZ3RoKHgpIDwgNCl7DQogICAgcmV0dXJuKE5BX2ludGVnZXJfKQ0KICB9DQogIA0KICBxMSA8LSBxdWFudGlsZSh4LCAwLjI1LCBuYS5ybSA9IFRSVUUpDQogIHEzIDwtIHF1YW50aWxlKHgsIDAuNzUsIG5hLnJtID0gVFJVRSkNCiAgaXFyIDwtIHEzIC0gcTENCiAgDQogIGxpbWl0ZV9pbmZlcmlvciA8LSBxMSAtIDEuNSAqIGlxcg0KICBsaW1pdGVfc3VwZXJpb3IgPC0gcTMgKyAxLjUgKiBpcXINCiAgDQogIHN1bSgNCiAgICB4IDwgbGltaXRlX2luZmVyaW9yIHwNCiAgICAgIHggPiBsaW1pdGVfc3VwZXJpb3IsDQogICAgbmEucm0gPSBUUlVFDQogICkNCn0NCg0KDQpzaGFwaXJvX3Bfc2VndXJvIDwtIGZ1bmN0aW9uKHgpew0KICANCiAgeCA8LSB4WyFpcy5uYSh4KV0NCiAgDQogIGlmKA0KICAgIGxlbmd0aCh4KSA8IDMgfHwNCiAgICBsZW5ndGgoeCkgPiA1MDAwIHx8DQogICAgbGVuZ3RoKHVuaXF1ZSh4KSkgPCAzDQogICl7DQogICAgcmV0dXJuKE5BX3JlYWxfKQ0KICB9DQogIA0KICB0cnlDYXRjaCgNCiAgICBzaGFwaXJvLnRlc3QoeCkkcC52YWx1ZSwNCiAgICBlcnJvciA9IGZ1bmN0aW9uKGUpIE5BX3JlYWxfDQogICkNCn0NCg0KDQpkaWFnbm9zdGljb19kaXN0cmlidWNpb25fdGFibGExX3RvdGFsIDwtIHB1cnJyOjptYXAyX2RmcigNCiAgdmFyc190YWJsYTFfbnVtZXJpY2FzLA0KICBldGlxdWV0YXNfdGFibGExX251bWVyaWNhcywNCiAgZnVuY3Rpb24odiwgZXRpcXVldGEpew0KICAgIA0KICAgIHggPC0gZXZhbHVhY2lvbmVzX2FuYWxpc2lzICU+JQ0KICAgICAgcHVsbCguZGF0YVtbdl1dKSAlPiUNCiAgICAgIG5hLm9taXQoKQ0KICAgIA0KICAgIHRpYmJsZTo6dGliYmxlKA0KICAgICAgdmFyaWFibGUgPSBldGlxdWV0YSwNCiAgICAgIG5fZXZhbHVhY2lvbmVzID0gbGVuZ3RoKHgpLA0KICAgICAgbWVkaWEgPSBtZWFuKHgsIG5hLnJtID0gVFJVRSksDQogICAgICBkZSA9IHNkKHgsIG5hLnJtID0gVFJVRSksDQogICAgICBtZWRpYW5hID0gbWVkaWFuKHgsIG5hLnJtID0gVFJVRSksDQogICAgICBwMjUgPSBxdWFudGlsZSh4LCAwLjI1LCBuYS5ybSA9IFRSVUUpLA0KICAgICAgcDc1ID0gcXVhbnRpbGUoeCwgMC43NSwgbmEucm0gPSBUUlVFKSwNCiAgICAgIGlxciA9IElRUih4LCBuYS5ybSA9IFRSVUUpLA0KICAgICAgbWluaW1vID0gbWluKHgsIG5hLnJtID0gVFJVRSksDQogICAgICBtYXhpbW8gPSBtYXgoeCwgbmEucm0gPSBUUlVFKSwNCiAgICAgIGFzaW1ldHJpYV9wZWFyc29uID0gYXNpbWV0cmlhX3BlYXJzb24oeCksDQogICAgICBuX291dGxpZXJzX2lxciA9IGNvbnRhcl9vdXRsaWVyc19pcXIoeCksDQogICAgICBzaGFwaXJvX3AgPSBzaGFwaXJvX3Bfc2VndXJvKHgpDQogICAgKQ0KICB9DQopICU+JQ0KICBtdXRhdGUoDQogICAgc2hhcGlyb19wX2Zvcm1hdGVhZG8gPSBjYXNlX3doZW4oDQogICAgICBpcy5uYShzaGFwaXJvX3ApIH4gTkFfY2hhcmFjdGVyXywNCiAgICAgIHNoYXBpcm9fcCA8IDAuMDAxIH4gIjwwLDAwMSIsDQogICAgICBUUlVFIH4gZm9ybWF0QygNCiAgICAgICAgc2hhcGlyb19wLA0KICAgICAgICBmb3JtYXQgPSAiZiIsDQogICAgICAgIGRpZ2l0cyA9IDMsDQogICAgICAgIGRlY2ltYWwubWFyayA9ICIsIg0KICAgICAgKQ0KICAgICksDQogICAgDQogICAgaW50ZXJwcmV0YWNpb25fc2hhcGlybyA9IGNhc2Vfd2hlbigNCiAgICAgIGlzLm5hKHNoYXBpcm9fcCkgfiAiTm8gZXZhbHVhYmxlIiwNCiAgICAgIHNoYXBpcm9fcCA8IDAuMDUgfiAiRXZpZGVuY2lhIGNvbnRyYSBub3JtYWxpZGFkIiwNCiAgICAgIFRSVUUgfiAiU2luIGV2aWRlbmNpYSBjbGFyYSBjb250cmEgbm9ybWFsaWRhZCINCiAgICApLA0KICAgIA0KICAgIGludGVycHJldGFjaW9uX2FzaW1ldHJpYSA9IGNhc2Vfd2hlbigNCiAgICAgIGlzLm5hKGFzaW1ldHJpYV9wZWFyc29uKSB+ICJObyBldmFsdWFibGUiLA0KICAgICAgYWJzKGFzaW1ldHJpYV9wZWFyc29uKSA8IDAuNSB+ICJBc2ltZXRyw61hIGJhamEiLA0KICAgICAgYWJzKGFzaW1ldHJpYV9wZWFyc29uKSA+PSAwLjUgJg0KICAgICAgICBhYnMoYXNpbWV0cmlhX3BlYXJzb24pIDwgMSB+ICJBc2ltZXRyw61hIG1vZGVyYWRhIiwNCiAgICAgIGFicyhhc2ltZXRyaWFfcGVhcnNvbikgPj0gMSB+ICJBc2ltZXRyw61hIG1hcmNhZGEiDQogICAgKQ0KICApDQoNCmRpYWdub3N0aWNvX2Rpc3RyaWJ1Y2lvbl90YWJsYTFfdG90YWwNCg0KDQpkaWFnbm9zdGljb19kaXN0cmlidWNpb25fdGFibGExX3Bvcl9ncnVwbyA8LSBldmFsdWFjaW9uZXNfYW5hbGlzaXMgJT4lDQogIHNlbGVjdCgNCiAgICBpZCwNCiAgICBncnVwb19jb21wZXRlbmNpYSwNCiAgICBhbGxfb2YodmFyc190YWJsYTFfbnVtZXJpY2FzKQ0KICApICU+JQ0KICBwaXZvdF9sb25nZXIoDQogICAgY29scyA9IGFsbF9vZih2YXJzX3RhYmxhMV9udW1lcmljYXMpLA0KICAgIG5hbWVzX3RvID0gInZhcmlhYmxlX29yaWdpbmFsIiwNCiAgICB2YWx1ZXNfdG8gPSAidmFsb3IiDQogICkgJT4lDQogIGZpbHRlcighaXMubmEodmFsb3IpKSAlPiUNCiAgbXV0YXRlKA0KICAgIHZhcmlhYmxlID0gY2FzZV93aGVuKA0KICAgICAgdmFyaWFibGVfb3JpZ2luYWwgPT0gImVkYWRfZXZhbHVhY2lvbiIgfiANCiAgICAgICAgIkVkYWQgYWwgbW9tZW50byBkZSBsYSBldmFsdWFjacOzbiwgYcOxb3MiLA0KICAgICAgdmFyaWFibGVfb3JpZ2luYWwgPT0gImFsdHVyYSIgfiANCiAgICAgICAgIkFsdHVyYSwgY20iLA0KICAgICAgdmFyaWFibGVfb3JpZ2luYWwgPT0gInBlc28iIH4gDQogICAgICAgICJQZXNvLCBrZyIsDQogICAgICB2YXJpYWJsZV9vcmlnaW5hbCA9PSAibW9tZW50b19wb3BfbWVzZXMiIH4gDQogICAgICAgICJNb21lbnRvIHBvc3RxdWlyw7pyZ2ljbywgbWVzZXMiLA0KICAgICAgVFJVRSB+IHZhcmlhYmxlX29yaWdpbmFsDQogICAgKQ0KICApICU+JQ0KICBncm91cF9ieSgNCiAgICB2YXJpYWJsZSwNCiAgICBncnVwb19jb21wZXRlbmNpYQ0KICApICU+JQ0KICBzdW1tYXJpc2UoDQogICAgbl9ldmFsdWFjaW9uZXMgPSBuKCksDQogICAgbl9qdWdhZG9yZXMgPSBuX2Rpc3RpbmN0KGlkKSwNCiAgICBtZWRpYSA9IG1lYW4odmFsb3IsIG5hLnJtID0gVFJVRSksDQogICAgZGUgPSBzZCh2YWxvciwgbmEucm0gPSBUUlVFKSwNCiAgICBtZWRpYW5hID0gbWVkaWFuKHZhbG9yLCBuYS5ybSA9IFRSVUUpLA0KICAgIHAyNSA9IHF1YW50aWxlKHZhbG9yLCAwLjI1LCBuYS5ybSA9IFRSVUUpLA0KICAgIHA3NSA9IHF1YW50aWxlKHZhbG9yLCAwLjc1LCBuYS5ybSA9IFRSVUUpLA0KICAgIGlxciA9IElRUih2YWxvciwgbmEucm0gPSBUUlVFKSwNCiAgICBtaW5pbW8gPSBtaW4odmFsb3IsIG5hLnJtID0gVFJVRSksDQogICAgbWF4aW1vID0gbWF4KHZhbG9yLCBuYS5ybSA9IFRSVUUpLA0KICAgIGFzaW1ldHJpYV9wZWFyc29uID0gYXNpbWV0cmlhX3BlYXJzb24odmFsb3IpLA0KICAgIG5fb3V0bGllcnNfaXFyID0gY29udGFyX291dGxpZXJzX2lxcih2YWxvciksDQogICAgc2hhcGlyb19wID0gc2hhcGlyb19wX3NlZ3Vybyh2YWxvciksDQogICAgLmdyb3VwcyA9ICJkcm9wIg0KICApICU+JQ0KICBtdXRhdGUoDQogICAgc2hhcGlyb19wX2Zvcm1hdGVhZG8gPSBjYXNlX3doZW4oDQogICAgICBpcy5uYShzaGFwaXJvX3ApIH4gTkFfY2hhcmFjdGVyXywNCiAgICAgIHNoYXBpcm9fcCA8IDAuMDAxIH4gIjwwLDAwMSIsDQogICAgICBUUlVFIH4gZm9ybWF0QygNCiAgICAgICAgc2hhcGlyb19wLA0KICAgICAgICBmb3JtYXQgPSAiZiIsDQogICAgICAgIGRpZ2l0cyA9IDMsDQogICAgICAgIGRlY2ltYWwubWFyayA9ICIsIg0KICAgICAgKQ0KICAgICksDQogICAgDQogICAgaW50ZXJwcmV0YWNpb25fc2hhcGlybyA9IGNhc2Vfd2hlbigNCiAgICAgIGlzLm5hKHNoYXBpcm9fcCkgfiAiTm8gZXZhbHVhYmxlIiwNCiAgICAgIHNoYXBpcm9fcCA8IDAuMDUgfiAiRXZpZGVuY2lhIGNvbnRyYSBub3JtYWxpZGFkIiwNCiAgICAgIFRSVUUgfiAiU2luIGV2aWRlbmNpYSBjbGFyYSBjb250cmEgbm9ybWFsaWRhZCINCiAgICApLA0KICAgIA0KICAgIGludGVycHJldGFjaW9uX2FzaW1ldHJpYSA9IGNhc2Vfd2hlbigNCiAgICAgIGlzLm5hKGFzaW1ldHJpYV9wZWFyc29uKSB+ICJObyBldmFsdWFibGUiLA0KICAgICAgYWJzKGFzaW1ldHJpYV9wZWFyc29uKSA8IDAuNSB+ICJBc2ltZXRyw61hIGJhamEiLA0KICAgICAgYWJzKGFzaW1ldHJpYV9wZWFyc29uKSA+PSAwLjUgJg0KICAgICAgICBhYnMoYXNpbWV0cmlhX3BlYXJzb24pIDwgMSB+ICJBc2ltZXRyw61hIG1vZGVyYWRhIiwNCiAgICAgIGFicyhhc2ltZXRyaWFfcGVhcnNvbikgPj0gMSB+ICJBc2ltZXRyw61hIG1hcmNhZGEiDQogICAgKQ0KICApDQoNCmRpYWdub3N0aWNvX2Rpc3RyaWJ1Y2lvbl90YWJsYTFfcG9yX2dydXBvDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDE4LjEgSGlzdG9ncmFtYXMgeSBncsOhZmljb3MgUS1RIHBhcmEgVGFibGEgMQ0KDQpgYGB7ciBncmFmaWNvc19kaXN0cmlidWNpb25fdGFibGExfQ0KZGF0b3NfdGFibGExX2xhcmdvIDwtIGV2YWx1YWNpb25lc19hbmFsaXNpcyAlPiUNCiAgc2VsZWN0KA0KICAgIGdydXBvX2NvbXBldGVuY2lhLA0KICAgIGFsbF9vZih2YXJzX3RhYmxhMV9udW1lcmljYXMpDQogICkgJT4lDQogIHBpdm90X2xvbmdlcigNCiAgICBjb2xzID0gYWxsX29mKHZhcnNfdGFibGExX251bWVyaWNhcyksDQogICAgbmFtZXNfdG8gPSAidmFyaWFibGVfb3JpZ2luYWwiLA0KICAgIHZhbHVlc190byA9ICJ2YWxvciINCiAgKSAlPiUNCiAgZmlsdGVyKCFpcy5uYSh2YWxvcikpICU+JQ0KICBtdXRhdGUoDQogICAgdmFyaWFibGUgPSBjYXNlX3doZW4oDQogICAgICB2YXJpYWJsZV9vcmlnaW5hbCA9PSAiZWRhZF9ldmFsdWFjaW9uIiB+IA0KICAgICAgICAiRWRhZCBhbCBtb21lbnRvIGRlIGxhIGV2YWx1YWNpw7NuLCBhw7FvcyIsDQogICAgICB2YXJpYWJsZV9vcmlnaW5hbCA9PSAiYWx0dXJhIiB+IA0KICAgICAgICAiQWx0dXJhLCBjbSIsDQogICAgICB2YXJpYWJsZV9vcmlnaW5hbCA9PSAicGVzbyIgfiANCiAgICAgICAgIlBlc28sIGtnIiwNCiAgICAgIHZhcmlhYmxlX29yaWdpbmFsID09ICJtb21lbnRvX3BvcF9tZXNlcyIgfiANCiAgICAgICAgIk1vbWVudG8gcG9zdHF1aXLDunJnaWNvLCBtZXNlcyIsDQogICAgICBUUlVFIH4gdmFyaWFibGVfb3JpZ2luYWwNCiAgICApDQogICkNCg0KDQpncmFmaWNvc19oaXN0b2dyYW1hc190YWJsYTEgPC0gZGF0b3NfdGFibGExX2xhcmdvICU+JQ0KICBzcGxpdCguJHZhcmlhYmxlKSAlPiUNCiAgcHVycnI6Om1hcCgNCiAgICB+IGdncGxvdCgNCiAgICAgIC54LA0KICAgICAgYWVzKHggPSB2YWxvcikNCiAgICApICsNCiAgICAgIGdlb21faGlzdG9ncmFtKA0KICAgICAgICBiaW5zID0gMTAsDQogICAgICAgIGNvbG9yID0gImJsYWNrIiwNCiAgICAgICAgZmlsbCA9ICJncmV5ODAiDQogICAgICApICsNCiAgICAgIGZhY2V0X3dyYXAoDQogICAgICAgIH4gZ3J1cG9fY29tcGV0ZW5jaWEsDQogICAgICAgIHNjYWxlcyA9ICJmcmVlX3kiDQogICAgICApICsNCiAgICAgIGxhYnMoDQogICAgICAgIHRpdGxlID0gdW5pcXVlKC54JHZhcmlhYmxlKSwNCiAgICAgICAgeCA9ICJWYWxvciIsDQogICAgICAgIHkgPSAiRnJlY3VlbmNpYSINCiAgICAgICkgKw0KICAgICAgdGhlbWVfbWluaW1hbCgpDQogICkNCg0KDQpncmFmaWNvc19xcV90YWJsYTEgPC0gZGF0b3NfdGFibGExX2xhcmdvICU+JQ0KICBzcGxpdCguJHZhcmlhYmxlKSAlPiUNCiAgcHVycnI6Om1hcCgNCiAgICB+IGdncGxvdCgNCiAgICAgIC54LA0KICAgICAgYWVzKHNhbXBsZSA9IHZhbG9yKQ0KICAgICkgKw0KICAgICAgc3RhdF9xcSgpICsNCiAgICAgIHN0YXRfcXFfbGluZSgpICsNCiAgICAgIGZhY2V0X3dyYXAoDQogICAgICAgIH4gZ3J1cG9fY29tcGV0ZW5jaWEsDQogICAgICAgIHNjYWxlcyA9ICJmcmVlIg0KICAgICAgKSArDQogICAgICBsYWJzKA0KICAgICAgICB0aXRsZSA9IHBhc3RlMCgNCiAgICAgICAgICAiUS1RIHBsb3Q6ICIsDQogICAgICAgICAgdW5pcXVlKC54JHZhcmlhYmxlKQ0KICAgICAgICApLA0KICAgICAgICB4ID0gIkN1YW50aWxlcyB0ZcOzcmljb3MiLA0KICAgICAgICB5ID0gIkN1YW50aWxlcyBvYnNlcnZhZG9zIg0KICAgICAgKSArDQogICAgICB0aGVtZV9taW5pbWFsKCkNCiAgKQ0KDQoNCmdyYWZpY29zX2hpc3RvZ3JhbWFzX3RhYmxhMVtbIkVkYWQgYWwgbW9tZW50byBkZSBsYSBldmFsdWFjacOzbiwgYcOxb3MiXV0NCmdyYWZpY29zX2hpc3RvZ3JhbWFzX3RhYmxhMVtbIkFsdHVyYSwgY20iXV0NCmdyYWZpY29zX2hpc3RvZ3JhbWFzX3RhYmxhMVtbIlBlc28sIGtnIl1dDQpncmFmaWNvc19oaXN0b2dyYW1hc190YWJsYTFbWyJNb21lbnRvIHBvc3RxdWlyw7pyZ2ljbywgbWVzZXMiXV0NCg0KZ3JhZmljb3NfcXFfdGFibGExW1siRWRhZCBhbCBtb21lbnRvIGRlIGxhIGV2YWx1YWNpw7NuLCBhw7FvcyJdXQ0KZ3JhZmljb3NfcXFfdGFibGExW1siQWx0dXJhLCBjbSJdXQ0KZ3JhZmljb3NfcXFfdGFibGExW1siUGVzbywga2ciXV0NCmdyYWZpY29zX3FxX3RhYmxhMVtbIk1vbWVudG8gcG9zdHF1aXLDunJnaWNvLCBtZXNlcyJdXQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIDE5LiBUYWJsYSAyOiBkZXNjcmlwY2nDs24gZGUgdmFyaWFibGVzIGRlIGZ1ZXJ6YSBpc29jaW7DqXRpY2ENCg0KTGEgVGFibGEgMiByZXN1bWUgbGFzIG3DqXRyaWNhcyBkZSBmdWVyemEgeSBkZXNlbXBlw7FvIGlzb2NpbsOpdGljbyBzZWfDum46DQoNCi0gICBncnVwbyBjb21wZXRpdGl2bzsNCi0gICB2ZWxvY2lkYWQgZGUgZXZhbHVhY2nDs24uDQoNClNlIGluZm9ybWFuIHNpbXVsdMOhbmVhbWVudGU6DQoNCi0gICBtZWRpYSB5IGRlc3bDrW8gZXN0w6FuZGFyOw0KLSAgIG1lZGlhbmEgeSBQMjXigJNQNzU7DQotICAgSVFSOw0KLSAgIG3DrW5pbW87DQotICAgbcOheGltbzsNCi0gICBjYW50aWRhZCBkZSBldmFsdWFjaW9uZXMgZGlzcG9uaWJsZXM7DQotICAgY2FudGlkYWQgZGUganVnYWRvcmVzIHF1ZSBhcG9ydGFyb24gZGF0b3MuDQoNCkVzdGEgZXN0cmF0ZWdpYSBldml0YSB0ZW5lciBxdWUgZGVmaW5pciwgcGFyYSBjYWRhIHVuYSBkZSBsYXMgbcO6bHRpcGxlcyB2YXJpYWJsZXMgZGUgZnVlcnphLCB1biDDum5pY28gZXN0YWTDrXN0aWNvIGRlIHRlbmRlbmNpYSBjZW50cmFsLg0KDQpgYGB7ciB0YWJsYTJfZnVlcnphfQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMjEpIFRBQkxBIDIgLSBWQVJJQUJMRVMgREUgRlVFUlpBIElTT0NJTsOJVElDQQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KdmFyc19mdWVyemEgPC0gdW5pcXVlKGMoDQogIHZhcnNfbWV0cmljYXNfY29udGludWFzLA0KICB2YXJzX3JlcGV0aWNpb25lcywNCiAgdmFyc19zaW1ldHJpYXMNCikpDQoNCnZhcnNfZnVlcnphIDwtIHZhcnNfZnVlcnphWw0KICB2YXJzX2Z1ZXJ6YSAlaW4lIG5hbWVzKGRhdG9zX2FuYWxpc2lzKQ0KXQ0KDQoNCnZhcnNfZnVlcnphX25vX251bWVyaWNhcyA8LSB2YXJzX2Z1ZXJ6YVsNCiAgIXZhcHBseSgNCiAgICBkYXRvc19hbmFsaXNpc1t2YXJzX2Z1ZXJ6YV0sDQogICAgaXMubnVtZXJpYywNCiAgICBsb2dpY2FsKDEpDQogICkNCl0NCg0KaWYobGVuZ3RoKHZhcnNfZnVlcnphX25vX251bWVyaWNhcykgPiAwKXsNCiAgc3RvcCgNCiAgICBwYXN0ZTAoDQogICAgICAiSGF5IHZhcmlhYmxlcyBkZSBmdWVyemEgbm8gbnVtw6lyaWNhczogIiwNCiAgICAgIHBhc3RlKHZhcnNfZnVlcnphX25vX251bWVyaWNhcywgY29sbGFwc2UgPSAiLCAiKQ0KICAgICkNCiAgKQ0KfQ0KDQoNCmRhdG9zX2Z1ZXJ6YV9sYXJnbyA8LSBkYXRvc19hbmFsaXNpcyAlPiUNCiAgc2VsZWN0KA0KICAgIGlkLA0KICAgIHNlc2lvbiwNCiAgICB2ZWxvY2lkYWQsDQogICAgZ3J1cG9fY29tcGV0ZW5jaWEsDQogICAgYWxsX29mKHZhcnNfZnVlcnphKQ0KICApICU+JQ0KICBwaXZvdF9sb25nZXIoDQogICAgY29scyA9IGFsbF9vZih2YXJzX2Z1ZXJ6YSksDQogICAgbmFtZXNfdG8gPSAidmFyaWFibGUiLA0KICAgIHZhbHVlc190byA9ICJ2YWxvciINCiAgKSAlPiUNCiAgZmlsdGVyKA0KICAgICFpcy5uYSh2YWxvciksDQogICAgIWlzLm5hKGdydXBvX2NvbXBldGVuY2lhKSwNCiAgICAhaXMubmEodmVsb2NpZGFkKQ0KICApDQoNCg0KdGFibGEyX2Z1ZXJ6YV9yZXN1bWVuX251bWVyaWNvIDwtIGRhdG9zX2Z1ZXJ6YV9sYXJnbyAlPiUNCiAgZ3JvdXBfYnkoDQogICAgdmFyaWFibGUsDQogICAgdmVsb2NpZGFkLA0KICAgIGdydXBvX2NvbXBldGVuY2lhDQogICkgJT4lDQogIHN1bW1hcmlzZSgNCiAgICANCiAgICBuX2V2YWx1YWNpb25lc19kaXNwb25pYmxlcyA9IG5fZGlzdGluY3QoDQogICAgICBwYXN0ZShpZCwgc2VzaW9uKQ0KICAgICksDQogICAgDQogICAgbl9qdWdhZG9yZXNfZGlzcG9uaWJsZXMgPSBuX2Rpc3RpbmN0KGlkKSwNCiAgICANCiAgICBtZWRpYSA9IG1lYW4odmFsb3IsIG5hLnJtID0gVFJVRSksDQogICAgZGUgPSBzZCh2YWxvciwgbmEucm0gPSBUUlVFKSwNCiAgICANCiAgICBtZWRpYW5hID0gbWVkaWFuKHZhbG9yLCBuYS5ybSA9IFRSVUUpLA0KICAgIHAyNSA9IHF1YW50aWxlKHZhbG9yLCAwLjI1LCBuYS5ybSA9IFRSVUUpLA0KICAgIHA3NSA9IHF1YW50aWxlKHZhbG9yLCAwLjc1LCBuYS5ybSA9IFRSVUUpLA0KICAgIGlxciA9IElRUih2YWxvciwgbmEucm0gPSBUUlVFKSwNCiAgICANCiAgICBtaW5pbW8gPSBtaW4odmFsb3IsIG5hLnJtID0gVFJVRSksDQogICAgbWF4aW1vID0gbWF4KHZhbG9yLCBuYS5ybSA9IFRSVUUpLA0KICAgIA0KICAgIC5ncm91cHMgPSAiZHJvcCINCiAgKQ0KDQoNCnRhYmxhMl9mdWVyemFfZm9ybWF0ZWFkYSA8LSB0YWJsYTJfZnVlcnphX3Jlc3VtZW5fbnVtZXJpY28gJT4lDQogIG11dGF0ZSgNCiAgICBtZWRpYV9kZSA9IGZvcm1hdGVhcl9tZWRpYV9kZSgNCiAgICAgIG1lZGlhLA0KICAgICAgZGUsDQogICAgICBkaWdpdG9zID0gMg0KICAgICksDQogICAgbWVkaWFuYV9wMjVfcDc1ID0gZm9ybWF0ZWFyX21lZGlhbmFfaXFyKA0KICAgICAgbWVkaWFuYSwNCiAgICAgIHAyNSwNCiAgICAgIHA3NSwNCiAgICAgIGRpZ2l0b3MgPSAyDQogICAgKQ0KICApICU+JQ0KICBzZWxlY3QoDQogICAgdmFyaWFibGUsDQogICAgdmVsb2NpZGFkLA0KICAgIGdydXBvX2NvbXBldGVuY2lhLA0KICAgIG5fZXZhbHVhY2lvbmVzX2Rpc3BvbmlibGVzLA0KICAgIG5fanVnYWRvcmVzX2Rpc3BvbmlibGVzLA0KICAgIG1lZGlhX2RlLA0KICAgIG1lZGlhbmFfcDI1X3A3NSwNCiAgICBpcXIsDQogICAgbWluaW1vLA0KICAgIG1heGltbw0KICApDQoNCnRhYmxhMl9mdWVyemFfZm9ybWF0ZWFkYQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIDIwLiBFeHBvcnRhY2nDs24gZGUgVGFibGEgMSB5IFRhYmxhIDIgYSBXb3JkDQoNClNlIGdlbmVyYSB1biBhcmNoaXZvIFdvcmQgY29uOg0KDQotICAgVGFibGEgMSwgZGl2aWRpZGEgZW46DQogICAgLSAgIFBhbmVsIEE6IHZhcmlhYmxlcyBudW3DqXJpY2FzOw0KICAgIC0gICBQYW5lbCBCOiB2YXJpYWJsZXMgY2F0ZWfDs3JpY2FzLg0KLSAgIFRhYmxhIDIgZGUgdmFyaWFibGVzIGRlIGZ1ZXJ6YS4NCg0KU2UgYWdyZWdhIHVuIGVww61ncmFmZSBleHBsaWNhdGl2byBzb2JyZSBsYSBjYW50aWRhZCBkZSBldmFsdWFjaW9uZXMgY2FsY3VsYWRhLg0KDQpgYGB7ciBleHBvcnRhcl93b3JkX3RhYmxhc30NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDIyKSBFWFBPUlRBUiBUQUJMQSAxIFkgVEFCTEEgMiBBIFdPUkQNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNCmV0aXF1ZXRhcl92YXJpYWJsZV9mdWVyemEgPC0gZnVuY3Rpb24oeCl7DQogIA0KICBldGlxdWV0YXMgPC0gYygNCiAgICBwaWNvX2RlbF9wYXJfZXh0ZW5zaW9uX25vX2ltcGxpY2FkbyA9ICJQaWNvIGRlIHBhciAtIGV4dGVuc2nDs24gLSBubyBpbXBsaWNhZG8iLA0KICAgIHBpY29fZGVsX3Bhcl9leHRlbnNpb25faW1wbGljYWRvID0gIlBpY28gZGUgcGFyIC0gZXh0ZW5zacOzbiAtIGltcGxpY2FkbyIsDQogICAgcGljb19kZWxfcGFyX2ZsZXhpb25fbm9faW1wbGljYWRvID0gIlBpY28gZGUgcGFyIC0gZmxleGnDs24gLSBubyBpbXBsaWNhZG8iLA0KICAgIHBpY29fZGVsX3Bhcl9mbGV4aW9uX2ltcGxpY2FkbyA9ICJQaWNvIGRlIHBhciAtIGZsZXhpw7NuIC0gaW1wbGljYWRvIiwNCiAgICANCiAgICB0cmFial9wZXNvX2NvcnBvcmFsX2V4dGVuc2lvbl9ub19pbXBsaWNhZG8gPSAiVHJhYmFqby9wZXNvIGNvcnBvcmFsIC0gZXh0ZW5zacOzbiAtIG5vIGltcGxpY2FkbyIsDQogICAgdHJhYmpfcGVzb19jb3Jwb3JhbF9leHRlbnNpb25faW1wbGljYWRvID0gIlRyYWJham8vcGVzbyBjb3Jwb3JhbCAtIGV4dGVuc2nDs24gLSBpbXBsaWNhZG8iLA0KICAgIHRyYWJqX3Blc29fY29ycG9yYWxfZmxleGlvbl9ub19pbXBsaWNhZG8gPSAiVHJhYmFqby9wZXNvIGNvcnBvcmFsIC0gZmxleGnDs24gLSBubyBpbXBsaWNhZG8iLA0KICAgIHRyYWJqX3Blc29fY29ycG9yYWxfZmxleGlvbl9pbXBsaWNhZG8gPSAiVHJhYmFqby9wZXNvIGNvcnBvcmFsIC0gZmxleGnDs24gLSBpbXBsaWNhZG8iLA0KICAgIA0KICAgIHRyYWJham9fdG90YWxfZXh0ZW5zaW9uX25vX2ltcGxpY2FkbyA9ICJUcmFiYWpvIHRvdGFsIC0gZXh0ZW5zacOzbiAtIG5vIGltcGxpY2FkbyIsDQogICAgdHJhYmFqb190b3RhbF9leHRlbnNpb25faW1wbGljYWRvID0gIlRyYWJham8gdG90YWwgLSBleHRlbnNpw7NuIC0gaW1wbGljYWRvIiwNCiAgICB0cmFiYWpvX3RvdGFsX2ZsZXhpb25fbm9faW1wbGljYWRvID0gIlRyYWJham8gdG90YWwgLSBmbGV4acOzbiAtIG5vIGltcGxpY2FkbyIsDQogICAgdHJhYmFqb190b3RhbF9mbGV4aW9uX2ltcGxpY2FkbyA9ICJUcmFiYWpvIHRvdGFsIC0gZmxleGnDs24gLSBpbXBsaWNhZG8iLA0KICAgIA0KICAgIHJhbmdvX2RlX21vdl9leHRlbnNpb25fbm9faW1wbGljYWRvID0gIlJhbmdvIGRlIG1vdmltaWVudG8gLSBleHRlbnNpw7NuIC0gbm8gaW1wbGljYWRvIiwNCiAgICByYW5nb19kZV9tb3ZfZXh0ZW5zaW9uX2ltcGxpY2FkbyA9ICJSYW5nbyBkZSBtb3ZpbWllbnRvIC0gZXh0ZW5zacOzbiAtIGltcGxpY2FkbyIsDQogICAgcmFuZ29fZGVfbW92X2ZsZXhpb25fbm9faW1wbGljYWRvID0gIlJhbmdvIGRlIG1vdmltaWVudG8gLSBmbGV4acOzbiAtIG5vIGltcGxpY2FkbyIsDQogICAgcmFuZ29fZGVfbW92X2ZsZXhpb25faW1wbGljYWRvID0gIlJhbmdvIGRlIG1vdmltaWVudG8gLSBmbGV4acOzbiAtIGltcGxpY2FkbyIsDQogICAgDQogICAgcmF6b25fYWdvbl9hbnRhZ29uX2V4dGVuc2lvbl9ub19pbXBsaWNhZG8gPSAiUmF6w7NuIGFnb25pc3RhL2FudGFnb25pc3RhIC0gZXh0ZW5zacOzbiAtIG5vIGltcGxpY2FkbyIsDQogICAgcmF6b25fYWdvbl9hbnRhZ29uX2V4dGVuc2lvbl9pbXBsaWNhZG8gPSAiUmF6w7NuIGFnb25pc3RhL2FudGFnb25pc3RhIC0gZXh0ZW5zacOzbiAtIGltcGxpY2FkbyIsDQogICAgcmF6b25fYWdvbl9hbnRhZ29uX2ZsZXhpb25fbm9faW1wbGljYWRvID0gIlJhesOzbiBhZ29uaXN0YS9hbnRhZ29uaXN0YSAtIGZsZXhpw7NuIC0gbm8gaW1wbGljYWRvIiwNCiAgICByYXpvbl9hZ29uX2FudGFnb25fZmxleGlvbl9pbXBsaWNhZG8gPSAiUmF6w7NuIGFnb25pc3RhL2FudGFnb25pc3RhIC0gZmxleGnDs24gLSBpbXBsaWNhZG8iLA0KICAgIA0KICAgIHNpbWV0cmlhX3BpY29fcGFyX2V4dGVuc2lvbiA9ICJTaW1ldHLDrWEgZGVsIHBpY28gZGUgcGFyIC0gZXh0ZW5zacOzbiIsDQogICAgc2ltZXRyaWFfcGljb19wYXJfZmxleGlvbiA9ICJTaW1ldHLDrWEgZGVsIHBpY28gZGUgcGFyIC0gZmxleGnDs24iLA0KICAgIHNpbWV0cmlhX3RyYWJqX3Blc29fY29ycG9yYWxfZXh0ZW5zaW9uID0gIlNpbWV0csOtYSBkZWwgdHJhYmFqby9wZXNvIGNvcnBvcmFsIC0gZXh0ZW5zacOzbiIsDQogICAgc2ltZXRyaWFfdHJhYmpfcGVzb19jb3Jwb3JhbF9mbGV4aW9uID0gIlNpbWV0csOtYSBkZWwgdHJhYmFqby9wZXNvIGNvcnBvcmFsIC0gZmxleGnDs24iLA0KICAgIHNpbWV0cmlhX3RyYWJham9fdG90YWxfZXh0ZW5zaW9uID0gIlNpbWV0csOtYSBkZWwgdHJhYmFqbyB0b3RhbCAtIGV4dGVuc2nDs24iLA0KICAgIHNpbWV0cmlhX3RyYWJham9fdG90YWxfZmxleGlvbiA9ICJTaW1ldHLDrWEgZGVsIHRyYWJham8gdG90YWwgLSBmbGV4acOzbiINCiAgKQ0KICANCiAgc2FsaWRhIDwtIHVubmFtZShldGlxdWV0YXNbeF0pDQogIA0KICBpZmVsc2UoDQogICAgaXMubmEoc2FsaWRhKSwNCiAgICBzdHJpbmdyOjpzdHJfdG9fc2VudGVuY2UoDQogICAgICBzdHJpbmdyOjpzdHJfcmVwbGFjZV9hbGwoeCwgIl8iLCAiICIpDQogICAgKSwNCiAgICBzYWxpZGENCiAgKQ0KfQ0KDQoNCnRhYmxhMV9udW1fd29yZCA8LSB0YWJsYTFfbnVtZXJpY2FzX2Zvcm1hdGVhZGEgJT4lDQogIG11dGF0ZSgNCiAgICBpcXIgPSByb3VuZChpcXIsIDIpLA0KICAgIG1pbmltbyA9IHJvdW5kKG1pbmltbywgMiksDQogICAgbWF4aW1vID0gcm91bmQobWF4aW1vLCAyKQ0KICApICU+JQ0KICByZW5hbWUoDQogICAgIlZhcmlhYmxlIiA9IHZhcmlhYmxlLA0KICAgICJHcnVwbyBjb21wZXRpdGl2byIgPSBncnVwb19jb21wZXRlbmNpYSwNCiAgICAiRXZhbHVhY2lvbmVzIGRpc3BvbmlibGVzKiIgPSBuX2V2YWx1YWNpb25lc19kaXNwb25pYmxlcywNCiAgICAiSnVnYWRvcmVzIGRpc3BvbmlibGVzIiA9IG5fanVnYWRvcmVzX2Rpc3BvbmlibGVzLA0KICAgICJNZWRpYSAoREUpIiA9IG1lZGlhX2RlLA0KICAgICJNZWRpYW5hIFtQMjU7IFA3NV0iID0gbWVkaWFuYV9wMjVfcDc1LA0KICAgICJJUVIiID0gaXFyLA0KICAgICJNw61uaW1vIiA9IG1pbmltbywNCiAgICAiTcOheGltbyIgPSBtYXhpbW8NCiAgKQ0KDQoNCnRhYmxhMV9jYXRfd29yZCA8LSB0YWJsYTFfY2F0ZWdvcmljYXNfZm9ybWF0ZWFkYSAlPiUNCiAgcmVuYW1lKA0KICAgICJWYXJpYWJsZSIgPSB2YXJpYWJsZSwNCiAgICAiQ2F0ZWdvcsOtYSIgPSBjYXRlZ29yaWEsDQogICAgIkdydXBvIGNvbXBldGl0aXZvIiA9IGdydXBvX2NvbXBldGVuY2lhLA0KICAgICJFdmFsdWFjaW9uZXMsIG4gKCUpIiA9IG5fcG9yY2VudGFqZV9ldmFsdWFjaW9uZXMsDQogICAgIkp1Z2Fkb3JlcyBlbiBjYXRlZ29yw61hIiA9IG5fanVnYWRvcmVzX2NhdGVnb3JpYSwNCiAgICAiRXZhbHVhY2lvbmVzIGRpc3BvbmlibGVzKiIgPSBuX2V2YWx1YWNpb25lc19kaXNwb25pYmxlc192YXJpYWJsZSwNCiAgICAiSnVnYWRvcmVzIGRpc3BvbmlibGVzIiA9IG5fanVnYWRvcmVzX2Rpc3BvbmlibGVzX3ZhcmlhYmxlDQogICkNCg0KDQp0YWJsYTJfd29yZCA8LSB0YWJsYTJfZnVlcnphX2Zvcm1hdGVhZGEgJT4lDQogIG11dGF0ZSgNCiAgICB2YXJpYWJsZSA9IGV0aXF1ZXRhcl92YXJpYWJsZV9mdWVyemEodmFyaWFibGUpLA0KICAgIHZlbG9jaWRhZCA9IHBhc3RlMChhcy5jaGFyYWN0ZXIodmVsb2NpZGFkKSwgIsKwL3MiKSwNCiAgICBpcXIgPSByb3VuZChpcXIsIDIpLA0KICAgIG1pbmltbyA9IHJvdW5kKG1pbmltbywgMiksDQogICAgbWF4aW1vID0gcm91bmQobWF4aW1vLCAyKQ0KICApICU+JQ0KICByZW5hbWUoDQogICAgIlZhcmlhYmxlIiA9IHZhcmlhYmxlLA0KICAgICJWZWxvY2lkYWQiID0gdmVsb2NpZGFkLA0KICAgICJHcnVwbyBjb21wZXRpdGl2byIgPSBncnVwb19jb21wZXRlbmNpYSwNCiAgICAiRXZhbHVhY2lvbmVzIGRpc3BvbmlibGVzKiIgPSBuX2V2YWx1YWNpb25lc19kaXNwb25pYmxlcywNCiAgICAiSnVnYWRvcmVzIGRpc3BvbmlibGVzIiA9IG5fanVnYWRvcmVzX2Rpc3BvbmlibGVzLA0KICAgICJNZWRpYSAoREUpIiA9IG1lZGlhX2RlLA0KICAgICJNZWRpYW5hIFtQMjU7IFA3NV0iID0gbWVkaWFuYV9wMjVfcDc1LA0KICAgICJJUVIiID0gaXFyLA0KICAgICJNw61uaW1vIiA9IG1pbmltbywNCiAgICAiTcOheGltbyIgPSBtYXhpbW8NCiAgKQ0KDQoNCmZvcm1hdGVhcl9mbGV4dGFibGUgPC0gZnVuY3Rpb24odGFibGEsIHRhbWFubyA9IDgpew0KICANCiAgZmxleHRhYmxlKHRhYmxhKSAlPiUNCiAgICB0aGVtZV9ib29rdGFicygpICU+JQ0KICAgIGF1dG9maXQoKSAlPiUNCiAgICBmb250c2l6ZShzaXplID0gdGFtYW5vLCBwYXJ0ID0gImFsbCIpICU+JQ0KICAgIGJvbGQocGFydCA9ICJoZWFkZXIiKSAlPiUNCiAgICBhbGlnbihhbGlnbiA9ICJjZW50ZXIiLCBwYXJ0ID0gImFsbCIpDQp9DQoNCg0KZnRfdGFibGExX251bSA8LSBmb3JtYXRlYXJfZmxleHRhYmxlKHRhYmxhMV9udW1fd29yZCwgdGFtYW5vID0gOCkNCmZ0X3RhYmxhMV9jYXQgPC0gZm9ybWF0ZWFyX2ZsZXh0YWJsZSh0YWJsYTFfY2F0X3dvcmQsIHRhbWFubyA9IDgpDQpmdF90YWJsYTIgPC0gZm9ybWF0ZWFyX2ZsZXh0YWJsZSh0YWJsYTJfd29yZCwgdGFtYW5vID0gNykNCg0KDQphcmNoaXZvX3dvcmRfdGFibGFzIDwtIGZpbGUucGF0aCgNCiAgZ2V0d2QoKSwNCiAgIlRhYmxhc18xX3lfMl9kZXNjcmlwdGl2YXNfaXNvY2luZXNpYV9STENBLmRvY3giDQopDQoNCg0KZG9jIDwtIHJlYWRfZG9jeCgpDQoNCmRvYyA8LSBib2R5X2FkZF9wYXIoDQogIGRvYywNCiAgIlRhYmxhcyBkZXNjcmlwdGl2YXMgZGVsIGFuw6FsaXNpcyBkZSBmdWVyemEgaXNvY2luw6l0aWNhIGVuIGZ1dGJvbGlzdGFzIGNvbiBSTENBIiwNCiAgc3R5bGUgPSAiaGVhZGluZyAxIg0KKQ0KDQpkb2MgPC0gYm9keV9hZGRfcGFyKA0KICBkb2MsDQogIHBhc3RlMCgNCiAgICAiQ29ob3J0ZSBhbmFsw610aWNhIGZpbmFsOiAiLA0KICAgIHJlc3VtZW5fbXVlc3RyYV9hbmFsaXRpY2Ekbl9qdWdhZG9yZXMsDQogICAgIiBqdWdhZG9yZXMsICIsDQogICAgcmVzdW1lbl9tdWVzdHJhX2FuYWxpdGljYSRuX2V2YWx1YWNpb25lcywNCiAgICAiIGV2YWx1YWNpb25lcyBjbMOtbmljYXMgw7puaWNhcyB5ICIsDQogICAgcmVzdW1lbl9tdWVzdHJhX2FuYWxpdGljYSRuX2ZpbGFzX2lzb2NpbmV0aWNhcywNCiAgICAiIHJlZ2lzdHJvcyBpc29jaW7DqXRpY29zIHBvciB2ZWxvY2lkYWQuIg0KICApLA0KICBzdHlsZSA9ICJOb3JtYWwiDQopDQoNCmRvYyA8LSBib2R5X2FkZF9wYXIoDQogIGRvYywNCiAgIlRhYmxhIDEuIENhcmFjdGVyw61zdGljYXMgZ2VuZXJhbGVzIGRlIGxhIGNvaG9ydGUgYW5hbMOtdGljYSIsDQogIHN0eWxlID0gImhlYWRpbmcgMSINCikNCg0KZG9jIDwtIGJvZHlfYWRkX3BhcigNCiAgZG9jLA0KICAiUGFuZWwgQS4gVmFyaWFibGVzIG51bcOpcmljYXMuIiwNCiAgc3R5bGUgPSAiaGVhZGluZyAyIg0KKQ0KDQpkb2MgPC0gYm9keV9hZGRfZmxleHRhYmxlKA0KICBkb2MsDQogIHZhbHVlID0gZnRfdGFibGExX251bQ0KKQ0KDQpkb2MgPC0gYm9keV9hZGRfcGFyKA0KICBkb2MsDQogICIqIEV2YWx1YWNpb25lcyBkaXNwb25pYmxlcyBjb3JyZXNwb25kZSBhIGNvbWJpbmFjaW9uZXMgw7puaWNhcyBkZSBqdWdhZG9yIHkgZmVjaGEgZGUgc2VzacOzbiAoSUQgw5cgc2VzacOzbikuIFVuIG1pc21vIGp1Z2Fkb3IgcHVkbyBhcG9ydGFyIG3DoXMgZGUgdW5hIGV2YWx1YWNpw7NuIHNpIGZ1ZSBtZWRpZG8gZW4gZGlzdGludG9zIG1vbWVudG9zIHBvc3RvcGVyYXRvcmlvcy4iLA0KICBzdHlsZSA9ICJOb3JtYWwiDQopDQoNCmRvYyA8LSBib2R5X2FkZF9wYXIoDQogIGRvYywNCiAgIlBhbmVsIEIuIFZhcmlhYmxlcyBjYXRlZ8OzcmljYXMuIiwNCiAgc3R5bGUgPSAiaGVhZGluZyAyIg0KKQ0KDQpkb2MgPC0gYm9keV9hZGRfZmxleHRhYmxlKA0KICBkb2MsDQogIHZhbHVlID0gZnRfdGFibGExX2NhdA0KKQ0KDQpkb2MgPC0gYm9keV9hZGRfcGFyKA0KICBkb2MsDQogICIqIExvcyBwb3JjZW50YWplcyBzZSBjYWxjdWxhcm9uIHNvYnJlIGxhcyBldmFsdWFjaW9uZXMgY29uIGRhdG8gZGlzcG9uaWJsZSBwYXJhIGNhZGEgdmFyaWFibGUsIGV4Y2x1eWVuZG8gbG9zIGZhbHRhbnRlcy4iLA0KICBzdHlsZSA9ICJOb3JtYWwiDQopDQoNCmRvYyA8LSBib2R5X2FkZF9wYXIoDQogIGRvYywNCiAgIlRhYmxhIDIuIERlc2NyaXBjacOzbiBkZSBsYXMgdmFyaWFibGVzIGRlIGZ1ZXJ6YSBpc29jaW7DqXRpY2Egc2Vnw7puIHZlbG9jaWRhZCB5IG5pdmVsIGNvbXBldGl0aXZvIiwNCiAgc3R5bGUgPSAiaGVhZGluZyAxIg0KKQ0KDQpkb2MgPC0gYm9keV9hZGRfZmxleHRhYmxlKA0KICBkb2MsDQogIHZhbHVlID0gZnRfdGFibGEyDQopDQoNCmRvYyA8LSBib2R5X2FkZF9wYXIoDQogIGRvYywNCiAgIiogRXZhbHVhY2lvbmVzIGRpc3BvbmlibGVzIGNvcnJlc3BvbmRlIGFsIG7Dum1lcm8gZGUgZXZhbHVhY2lvbmVzIGNsw61uaWNhcyDDum5pY2FzIHV0aWxpemFkYXMgcGFyYSBjYWxjdWxhciBjYWRhIHZhcmlhYmxlIGRlbnRybyBkZSBjYWRhIHZlbG9jaWRhZCB5IGdydXBvIGNvbXBldGl0aXZvLiBVbiBtaXNtbyBqdWdhZG9yIHB1ZG8gYXBvcnRhciBtw6FzIGRlIHVuYSBldmFsdWFjacOzbiBlbiBkaWZlcmVudGVzIG1vbWVudG9zIHBvc3RvcGVyYXRvcmlvcy4iLA0KICBzdHlsZSA9ICJOb3JtYWwiDQopDQoNCnByaW50KA0KICBkb2MsDQogIHRhcmdldCA9IGFyY2hpdm9fd29yZF90YWJsYXMNCikNCg0KYXJjaGl2b193b3JkX3RhYmxhcw0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIDIxLiBGaWd1cmFzIGRlc2NyaXB0aXZhcyBwcm9wdWVzdGFzDQoNCiMjIEp1c3RpZmljYWNpw7NuIGRlbCBmb3JtYXRvIGdyw6FmaWNvDQoNClBhcmEgcmVwcmVzZW50YXIgdmFyaWFibGVzIGNvbnRpbnVhcyBzZSBwcm9wb25lIHV0aWxpemFyICoqcHVudG9zIGNvbiBiYXJyYXMgdmVydGljYWxlcyBQMjXigJNQNzUqKiwgZW4gbHVnYXIgZGUgYmFycmFzIG1hY2l6YXMuIEVzdGEgZWxlY2Npw7NuIHBlcm1pdGUgbW9zdHJhciBzaW11bHTDoW5lYW1lbnRlOg0KDQotICAgbGEgdGVuZGVuY2lhIGNlbnRyYWw7DQotICAgbGEgZGlzcGVyc2nDs24gaW50ZXJjdWFydMOtbGljYTsNCi0gICBsYSBjb21wYXJhY2nDs24gdmlzdWFsIGVudHJlIGdydXBvcyB5IHRpZW1wb3MuDQoNClNlIGRpc2XDsWFuIHRyZXMgZmlndXJhczoNCg0KMS4gICoqUGljbyBkZSBwYXIgZW4gZXh0ZW5zacOzbioqLCBjb24gcGFuZWxlcyBwYXJhIG1pZW1icm8gaW1wbGljYWRvIHkgbm8gaW1wbGljYWRvLg0KMi4gICoqUGljbyBkZSBwYXIgZW4gZmxleGnDs24qKiwgY29uIHBhbmVsZXMgcGFyYSBtaWVtYnJvIGltcGxpY2FkbyB5IG5vIGltcGxpY2Fkby4NCjMuICAqKlNpbWV0csOtYSBkZWwgcGljbyBkZSBwYXIqKiwgY29uIHBhbmVsZXMgcGFyYSBleHRlbnNpw7NuIHkgZmxleGnDs24uDQoNCkNhZGEgZmlndXJhIHNlIGVzdHJhdGlmaWNhIHBvcjoNCg0KLSAgIHZlbG9jaWRhZCBkZSBldmFsdWFjacOzbjsNCi0gICBtb21lbnRvIHBvc3RvcGVyYXRvcmlvOw0KLSAgIGdydXBvIGNvbXBldGl0aXZvLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMjEuMSBGdW5jacOzbiBhdXhpbGlhciBwYXJhIGdyw6FmaWNvcyBkZSBtZWRpYW5hIGUgSVFSDQoNCmBgYHtyIGZ1bmNpb25fZ3JhZmljb3N9DQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAyMykgRlVOQ0nDk04gQVVYSUxJQVIgUEFSQSBGSUdVUkFTIERFU0NSSVBUSVZBUw0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KcmVzdW1pcl9wYXJhX2ZpZ3VyYSA8LSBmdW5jdGlvbihkYXRhLCB2YXJpYWJsZV92YWxvcil7DQogIA0KICBkYXRhICU+JQ0KICAgIGZpbHRlcighaXMubmEoLmRhdGFbW3ZhcmlhYmxlX3ZhbG9yXV0pKSAlPiUNCiAgICBncm91cF9ieSgNCiAgICAgIHZlbG9jaWRhZCwNCiAgICAgIGdydXBvX2NvbXBldGVuY2lhLA0KICAgICAgY2F0ZWdvcmlhX21vbWVudG9fcG9wX2Rlcml2YWRhDQogICAgKSAlPiUNCiAgICBzdW1tYXJpc2UoDQogICAgICBtZWRpYW5hID0gbWVkaWFuKC5kYXRhW1t2YXJpYWJsZV92YWxvcl1dLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgcDI1ID0gcXVhbnRpbGUoLmRhdGFbW3ZhcmlhYmxlX3ZhbG9yXV0sIDAuMjUsIG5hLnJtID0gVFJVRSksDQogICAgICBwNzUgPSBxdWFudGlsZSguZGF0YVtbdmFyaWFibGVfdmFsb3JdXSwgMC43NSwgbmEucm0gPSBUUlVFKSwNCiAgICAgIG5fZXZhbHVhY2lvbmVzID0gbl9kaXN0aW5jdChwYXN0ZShpZCwgc2VzaW9uKSksDQogICAgICBuX2p1Z2Fkb3JlcyA9IG5fZGlzdGluY3QoaWQpLA0KICAgICAgLmdyb3VwcyA9ICJkcm9wIg0KICAgICkNCn0NCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMjEuMiBGaWd1cmEgMS4gUGljbyBkZSBwYXIgZW4gZXh0ZW5zacOzbjogaW1wbGljYWRvIHkgbm8gaW1wbGljYWRvDQoNCmBgYHtyIGZpZ3VyYV9leHRlbnNpb259DQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAyNCkgRklHVVJBIDEgLSBQSUNPIERFIFBBUiBFTiBFWFRFTlNJw5NODQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KDQpkYXRvc19maWd1cmFfZXh0ZW5zaW9uIDwtIGRhdG9zX2FuYWxpc2lzICU+JQ0KICBzZWxlY3QoDQogICAgaWQsDQogICAgc2VzaW9uLA0KICAgIHZlbG9jaWRhZCwNCiAgICBncnVwb19jb21wZXRlbmNpYSwNCiAgICBjYXRlZ29yaWFfbW9tZW50b19wb3BfZGVyaXZhZGEsDQogICAgcGljb19kZWxfcGFyX2V4dGVuc2lvbl9pbXBsaWNhZG8sDQogICAgcGljb19kZWxfcGFyX2V4dGVuc2lvbl9ub19pbXBsaWNhZG8NCiAgKSAlPiUNCiAgcGl2b3RfbG9uZ2VyKA0KICAgIGNvbHMgPSBjKA0KICAgICAgcGljb19kZWxfcGFyX2V4dGVuc2lvbl9pbXBsaWNhZG8sDQogICAgICBwaWNvX2RlbF9wYXJfZXh0ZW5zaW9uX25vX2ltcGxpY2Fkbw0KICAgICksDQogICAgbmFtZXNfdG8gPSAibWllbWJybyIsDQogICAgdmFsdWVzX3RvID0gInZhbG9yIg0KICApICU+JQ0KICBtdXRhdGUoDQogICAgbWllbWJybyA9IGNhc2Vfd2hlbigNCiAgICAgIG1pZW1icm8gPT0gInBpY29fZGVsX3Bhcl9leHRlbnNpb25faW1wbGljYWRvIiB+ICJJbXBsaWNhZG8iLA0KICAgICAgbWllbWJybyA9PSAicGljb19kZWxfcGFyX2V4dGVuc2lvbl9ub19pbXBsaWNhZG8iIH4gIk5vIGltcGxpY2FkbyIsDQogICAgICBUUlVFIH4gbWllbWJybw0KICAgICksDQogICAgbWllbWJybyA9IGZhY3RvcigNCiAgICAgIG1pZW1icm8sDQogICAgICBsZXZlbHMgPSBjKCJObyBpbXBsaWNhZG8iLCAiSW1wbGljYWRvIikNCiAgICApLA0KICAgIHZlbG9jaWRhZF9ldGlxdWV0YSA9IHBhc3RlMChhcy5jaGFyYWN0ZXIodmVsb2NpZGFkKSwgIsKwL3MiKQ0KICApICU+JQ0KICBmaWx0ZXIoIWlzLm5hKHZhbG9yKSkgJT4lDQogIGdyb3VwX2J5KA0KICAgIG1pZW1icm8sDQogICAgdmVsb2NpZGFkX2V0aXF1ZXRhLA0KICAgIGdydXBvX2NvbXBldGVuY2lhLA0KICAgIGNhdGVnb3JpYV9tb21lbnRvX3BvcF9kZXJpdmFkYQ0KICApICU+JQ0KICBzdW1tYXJpc2UoDQogICAgbWVkaWFuYSA9IG1lZGlhbih2YWxvciwgbmEucm0gPSBUUlVFKSwNCiAgICBwMjUgPSBxdWFudGlsZSh2YWxvciwgMC4yNSwgbmEucm0gPSBUUlVFKSwNCiAgICBwNzUgPSBxdWFudGlsZSh2YWxvciwgMC43NSwgbmEucm0gPSBUUlVFKSwNCiAgICAuZ3JvdXBzID0gImRyb3AiDQogICkNCg0KDQpmaWd1cmFfZXh0ZW5zaW9uIDwtIGdncGxvdCgNCiAgZGF0b3NfZmlndXJhX2V4dGVuc2lvbiwNCiAgYWVzKA0KICAgIHggPSBjYXRlZ29yaWFfbW9tZW50b19wb3BfZGVyaXZhZGEsDQogICAgeSA9IG1lZGlhbmEsDQogICAgY29sb3IgPSBncnVwb19jb21wZXRlbmNpYQ0KICApDQopICsNCiAgZ2VvbV9lcnJvcmJhcigNCiAgICBhZXMoDQogICAgICB5bWluID0gcDI1LA0KICAgICAgeW1heCA9IHA3NQ0KICAgICksDQogICAgd2lkdGggPSAwLjE4LA0KICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjU1KSwNCiAgICBsaW5ld2lkdGggPSAwLjcNCiAgKSArDQogIGdlb21fcG9pbnQoDQogICAgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuNTUpLA0KICAgIHNpemUgPSAyLjcNCiAgKSArDQogIGZhY2V0X2dyaWQoDQogICAgbWllbWJybyB+IHZlbG9jaWRhZF9ldGlxdWV0YQ0KICApICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJQaWNvIGRlIHBhciBlbiBleHRlbnNpw7NuIHNlZ8O6biBncnVwbyBjb21wZXRpdGl2byB5IG1vbWVudG8gcG9zdG9wZXJhdG9yaW8iLA0KICAgIHN1YnRpdGxlID0gIlB1bnRvczogbWVkaWFuYS4gQmFycmFzIHZlcnRpY2FsZXM6IFAyNeKAk1A3NS4iLA0KICAgIHggPSAiTW9tZW50byBwb3N0b3BlcmF0b3JpbyIsDQogICAgeSA9ICJQaWNvIGRlIHBhciBlbiBleHRlbnNpw7NuIiwNCiAgICBjb2xvciA9ICJHcnVwbyBjb21wZXRpdGl2byINCiAgKSArDQogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTEpICsNCiAgdGhlbWUoDQogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsDQogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIpDQogICkNCg0KZmlndXJhX2V4dGVuc2lvbg0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAyMS4zIEZpZ3VyYSAyLiBQaWNvIGRlIHBhciBlbiBmbGV4acOzbjogaW1wbGljYWRvIHkgbm8gaW1wbGljYWRvDQoNCmBgYHtyIGZpZ3VyYV9mbGV4aW9ufQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMjUpIEZJR1VSQSAyIC0gUElDTyBERSBQQVIgRU4gRkxFWEnDk04NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNCmRhdG9zX2ZpZ3VyYV9mbGV4aW9uIDwtIGRhdG9zX2FuYWxpc2lzICU+JQ0KICBzZWxlY3QoDQogICAgaWQsDQogICAgc2VzaW9uLA0KICAgIHZlbG9jaWRhZCwNCiAgICBncnVwb19jb21wZXRlbmNpYSwNCiAgICBjYXRlZ29yaWFfbW9tZW50b19wb3BfZGVyaXZhZGEsDQogICAgcGljb19kZWxfcGFyX2ZsZXhpb25faW1wbGljYWRvLA0KICAgIHBpY29fZGVsX3Bhcl9mbGV4aW9uX25vX2ltcGxpY2Fkbw0KICApICU+JQ0KICBwaXZvdF9sb25nZXIoDQogICAgY29scyA9IGMoDQogICAgICBwaWNvX2RlbF9wYXJfZmxleGlvbl9pbXBsaWNhZG8sDQogICAgICBwaWNvX2RlbF9wYXJfZmxleGlvbl9ub19pbXBsaWNhZG8NCiAgICApLA0KICAgIG5hbWVzX3RvID0gIm1pZW1icm8iLA0KICAgIHZhbHVlc190byA9ICJ2YWxvciINCiAgKSAlPiUNCiAgbXV0YXRlKA0KICAgIG1pZW1icm8gPSBjYXNlX3doZW4oDQogICAgICBtaWVtYnJvID09ICJwaWNvX2RlbF9wYXJfZmxleGlvbl9pbXBsaWNhZG8iIH4gIkltcGxpY2FkbyIsDQogICAgICBtaWVtYnJvID09ICJwaWNvX2RlbF9wYXJfZmxleGlvbl9ub19pbXBsaWNhZG8iIH4gIk5vIGltcGxpY2FkbyIsDQogICAgICBUUlVFIH4gbWllbWJybw0KICAgICksDQogICAgbWllbWJybyA9IGZhY3RvcigNCiAgICAgIG1pZW1icm8sDQogICAgICBsZXZlbHMgPSBjKCJObyBpbXBsaWNhZG8iLCAiSW1wbGljYWRvIikNCiAgICApLA0KICAgIHZlbG9jaWRhZF9ldGlxdWV0YSA9IHBhc3RlMChhcy5jaGFyYWN0ZXIodmVsb2NpZGFkKSwgIsKwL3MiKQ0KICApICU+JQ0KICBmaWx0ZXIoIWlzLm5hKHZhbG9yKSkgJT4lDQogIGdyb3VwX2J5KA0KICAgIG1pZW1icm8sDQogICAgdmVsb2NpZGFkX2V0aXF1ZXRhLA0KICAgIGdydXBvX2NvbXBldGVuY2lhLA0KICAgIGNhdGVnb3JpYV9tb21lbnRvX3BvcF9kZXJpdmFkYQ0KICApICU+JQ0KICBzdW1tYXJpc2UoDQogICAgbWVkaWFuYSA9IG1lZGlhbih2YWxvciwgbmEucm0gPSBUUlVFKSwNCiAgICBwMjUgPSBxdWFudGlsZSh2YWxvciwgMC4yNSwgbmEucm0gPSBUUlVFKSwNCiAgICBwNzUgPSBxdWFudGlsZSh2YWxvciwgMC43NSwgbmEucm0gPSBUUlVFKSwNCiAgICAuZ3JvdXBzID0gImRyb3AiDQogICkNCg0KDQpmaWd1cmFfZmxleGlvbiA8LSBnZ3Bsb3QoDQogIGRhdG9zX2ZpZ3VyYV9mbGV4aW9uLA0KICBhZXMoDQogICAgeCA9IGNhdGVnb3JpYV9tb21lbnRvX3BvcF9kZXJpdmFkYSwNCiAgICB5ID0gbWVkaWFuYSwNCiAgICBjb2xvciA9IGdydXBvX2NvbXBldGVuY2lhDQogICkNCikgKw0KICBnZW9tX2Vycm9yYmFyKA0KICAgIGFlcygNCiAgICAgIHltaW4gPSBwMjUsDQogICAgICB5bWF4ID0gcDc1DQogICAgKSwNCiAgICB3aWR0aCA9IDAuMTgsDQogICAgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuNTUpLA0KICAgIGxpbmV3aWR0aCA9IDAuNw0KICApICsNCiAgZ2VvbV9wb2ludCgNCiAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC41NSksDQogICAgc2l6ZSA9IDIuNw0KICApICsNCiAgZmFjZXRfZ3JpZCgNCiAgICBtaWVtYnJvIH4gdmVsb2NpZGFkX2V0aXF1ZXRhDQogICkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIlBpY28gZGUgcGFyIGVuIGZsZXhpw7NuIHNlZ8O6biBncnVwbyBjb21wZXRpdGl2byB5IG1vbWVudG8gcG9zdG9wZXJhdG9yaW8iLA0KICAgIHN1YnRpdGxlID0gIlB1bnRvczogbWVkaWFuYS4gQmFycmFzIHZlcnRpY2FsZXM6IFAyNeKAk1A3NS4iLA0KICAgIHggPSAiTW9tZW50byBwb3N0b3BlcmF0b3JpbyIsDQogICAgeSA9ICJQaWNvIGRlIHBhciBlbiBmbGV4acOzbiIsDQogICAgY29sb3IgPSAiR3J1cG8gY29tcGV0aXRpdm8iDQogICkgKw0KICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDExKSArDQogIHRoZW1lKA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLA0KICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksDQogICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiKQ0KICApDQoNCmZpZ3VyYV9mbGV4aW9uDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDIxLjQgRmlndXJhIDMuIFNpbWV0csOtYSBkZWwgcGljbyBkZSBwYXIgZW4gZXh0ZW5zacOzbiB5IGZsZXhpw7NuDQoNCmBgYHtyIGZpZ3VyYV9zaW1ldHJpYX0NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDI2KSBGSUdVUkEgMyAtIFNJTUVUUsONQSBERUwgUElDTyBERSBQQVINCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNCmRhdG9zX2ZpZ3VyYV9zaW1ldHJpYSA8LSBkYXRvc19hbmFsaXNpcyAlPiUNCiAgc2VsZWN0KA0KICAgIGlkLA0KICAgIHNlc2lvbiwNCiAgICB2ZWxvY2lkYWQsDQogICAgZ3J1cG9fY29tcGV0ZW5jaWEsDQogICAgY2F0ZWdvcmlhX21vbWVudG9fcG9wX2Rlcml2YWRhLA0KICAgIHNpbWV0cmlhX3BpY29fcGFyX2V4dGVuc2lvbiwNCiAgICBzaW1ldHJpYV9waWNvX3Bhcl9mbGV4aW9uDQogICkgJT4lDQogIHBpdm90X2xvbmdlcigNCiAgICBjb2xzID0gYygNCiAgICAgIHNpbWV0cmlhX3BpY29fcGFyX2V4dGVuc2lvbiwNCiAgICAgIHNpbWV0cmlhX3BpY29fcGFyX2ZsZXhpb24NCiAgICApLA0KICAgIG5hbWVzX3RvID0gIm1vdmltaWVudG8iLA0KICAgIHZhbHVlc190byA9ICJ2YWxvciINCiAgKSAlPiUNCiAgbXV0YXRlKA0KICAgIG1vdmltaWVudG8gPSBjYXNlX3doZW4oDQogICAgICBtb3ZpbWllbnRvID09ICJzaW1ldHJpYV9waWNvX3Bhcl9leHRlbnNpb24iIH4gIkV4dGVuc2nDs24iLA0KICAgICAgbW92aW1pZW50byA9PSAic2ltZXRyaWFfcGljb19wYXJfZmxleGlvbiIgfiAiRmxleGnDs24iLA0KICAgICAgVFJVRSB+IG1vdmltaWVudG8NCiAgICApLA0KICAgIG1vdmltaWVudG8gPSBmYWN0b3IoDQogICAgICBtb3ZpbWllbnRvLA0KICAgICAgbGV2ZWxzID0gYygiRXh0ZW5zacOzbiIsICJGbGV4acOzbiIpDQogICAgKSwNCiAgICB2ZWxvY2lkYWRfZXRpcXVldGEgPSBwYXN0ZTAoYXMuY2hhcmFjdGVyKHZlbG9jaWRhZCksICLCsC9zIikNCiAgKSAlPiUNCiAgZmlsdGVyKCFpcy5uYSh2YWxvcikpICU+JQ0KICBncm91cF9ieSgNCiAgICBtb3ZpbWllbnRvLA0KICAgIHZlbG9jaWRhZF9ldGlxdWV0YSwNCiAgICBncnVwb19jb21wZXRlbmNpYSwNCiAgICBjYXRlZ29yaWFfbW9tZW50b19wb3BfZGVyaXZhZGENCiAgKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIG1lZGlhbmEgPSBtZWRpYW4odmFsb3IsIG5hLnJtID0gVFJVRSksDQogICAgcDI1ID0gcXVhbnRpbGUodmFsb3IsIDAuMjUsIG5hLnJtID0gVFJVRSksDQogICAgcDc1ID0gcXVhbnRpbGUodmFsb3IsIDAuNzUsIG5hLnJtID0gVFJVRSksDQogICAgLmdyb3VwcyA9ICJkcm9wIg0KICApDQoNCg0KZmlndXJhX3NpbWV0cmlhIDwtIGdncGxvdCgNCiAgZGF0b3NfZmlndXJhX3NpbWV0cmlhLA0KICBhZXMoDQogICAgeCA9IGNhdGVnb3JpYV9tb21lbnRvX3BvcF9kZXJpdmFkYSwNCiAgICB5ID0gbWVkaWFuYSwNCiAgICBjb2xvciA9IGdydXBvX2NvbXBldGVuY2lhDQogICkNCikgKw0KICBnZW9tX2hsaW5lKA0KICAgIHlpbnRlcmNlcHQgPSAxMDAsDQogICAgbGluZXR5cGUgPSAiZGFzaGVkIiwNCiAgICBjb2xvciA9ICJncmV5NTAiDQogICkgKw0KICBnZW9tX2Vycm9yYmFyKA0KICAgIGFlcygNCiAgICAgIHltaW4gPSBwMjUsDQogICAgICB5bWF4ID0gcDc1DQogICAgKSwNCiAgICB3aWR0aCA9IDAuMTgsDQogICAgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuNTUpLA0KICAgIGxpbmV3aWR0aCA9IDAuNw0KICApICsNCiAgZ2VvbV9wb2ludCgNCiAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC41NSksDQogICAgc2l6ZSA9IDIuNw0KICApICsNCiAgZmFjZXRfZ3JpZCgNCiAgICBtb3ZpbWllbnRvIH4gdmVsb2NpZGFkX2V0aXF1ZXRhDQogICkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIlNpbWV0csOtYSBkZWwgcGljbyBkZSBwYXIgc2Vnw7puIGdydXBvIGNvbXBldGl0aXZvIHkgbW9tZW50byBwb3N0b3BlcmF0b3JpbyIsDQogICAgc3VidGl0bGUgPSAiUHVudG9zOiBtZWRpYW5hLiBCYXJyYXMgdmVydGljYWxlczogUDI14oCTUDc1LiBMw61uZWEgZGlzY29udGludWE6IDEwMCUgZGUgc2ltZXRyw61hLiIsDQogICAgeCA9ICJNb21lbnRvIHBvc3RvcGVyYXRvcmlvIiwNCiAgICB5ID0gIlNpbWV0csOtYSBkZWwgcGljbyBkZSBwYXIgKCUpIiwNCiAgICBjb2xvciA9ICJHcnVwbyBjb21wZXRpdGl2byINCiAgKSArDQogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTEpICsNCiAgdGhlbWUoDQogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsDQogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIpDQogICkNCg0KZmlndXJhX3NpbWV0cmlhDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgMjIuIEd1YXJkYWRvIGRlIGZpZ3VyYXMNCg0KYGBge3IgZ3VhcmRhcl9maWd1cmFzfQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMjcpIEdVQVJEQVIgRklHVVJBUw0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KY2FycGV0YV9maWd1cmFzIDwtIGZpbGUucGF0aCgNCiAgZ2V0d2QoKSwNCiAgImZpZ3VyYXNfaXNvY2luZXNpYV9STENBIg0KKQ0KDQpkaXIuY3JlYXRlKA0KICBjYXJwZXRhX2ZpZ3VyYXMsDQogIHNob3dXYXJuaW5ncyA9IEZBTFNFDQopDQoNCmdnc2F2ZSgNCiAgZmlsZW5hbWUgPSBmaWxlLnBhdGgoDQogICAgY2FycGV0YV9maWd1cmFzLA0KICAgICJGaWd1cmFfMV9waWNvX3Bhcl9leHRlbnNpb24ucG5nIg0KICApLA0KICBwbG90ID0gZmlndXJhX2V4dGVuc2lvbiwNCiAgd2lkdGggPSAxMywNCiAgaGVpZ2h0ID0gNy41LA0KICBkcGkgPSAzMDANCikNCg0KZ2dzYXZlKA0KICBmaWxlbmFtZSA9IGZpbGUucGF0aCgNCiAgICBjYXJwZXRhX2ZpZ3VyYXMsDQogICAgIkZpZ3VyYV8yX3BpY29fcGFyX2ZsZXhpb24ucG5nIg0KICApLA0KICBwbG90ID0gZmlndXJhX2ZsZXhpb24sDQogIHdpZHRoID0gMTMsDQogIGhlaWdodCA9IDcuNSwNCiAgZHBpID0gMzAwDQopDQoNCmdnc2F2ZSgNCiAgZmlsZW5hbWUgPSBmaWxlLnBhdGgoDQogICAgY2FycGV0YV9maWd1cmFzLA0KICAgICJGaWd1cmFfM19zaW1ldHJpYV9waWNvX3Bhci5wbmciDQogICksDQogIHBsb3QgPSBmaWd1cmFfc2ltZXRyaWEsDQogIHdpZHRoID0gMTMsDQogIGhlaWdodCA9IDcuNSwNCiAgZHBpID0gMzAwDQopDQoNCmNhcnBldGFfZmlndXJhcw0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIDIzLiBTw61udGVzaXMgZmluYWwgZGVsIGFuw6FsaXNpcyBkZXNjcmlwdGl2bw0KDQpFbCBhbsOhbGlzaXMgcGVybWl0acOzIGNvbnN0cnVpciB1bmEgY29ob3J0ZSBhbmFsw610aWNhIGRlcHVyYWRhIGRlICoqYHIgcmVzdW1lbl9tdWVzdHJhX2FuYWxpdGljYSRuX2p1Z2Fkb3Jlc2AganVnYWRvcmVzKiogeSAqKmByIHJlc3VtZW5fbXVlc3RyYV9hbmFsaXRpY2Ekbl9ldmFsdWFjaW9uZXNgIGV2YWx1YWNpb25lcyBjbMOtbmljYXMgw7puaWNhcyoqLCBleGNsdXllbmRvIGp1Z2Fkb3JlcyBjb24gcmVsZXNpw7NuLCBldmFsdWFjaW9uZXMgc2luIG1vbWVudG8gcG9zdG9wZXJhdG9yaW8geSBldmFsdWFjaW9uZXMgcmVhbGl6YWRhcyBtw6FzIGFsbMOhIGRlIGxvcyAxOCBtZXNlcyBwb3N0ZXJpb3JlcyBhIGxhIGNpcnVnw61hLg0KDQpMYSBUYWJsYSAxIHJlc3VtZSBsYXMgY2FyYWN0ZXLDrXN0aWNhcyBnZW5lcmFsZXMgZGUgbGEgY29ob3J0ZSBzZWfDum4gbml2ZWwgY29tcGV0aXRpdm8uIExhcyB2YXJpYWJsZXMgbnVtw6lyaWNhcyBzZSBwcmVzZW50YW4gZW4gZm9ybWF0byBhbXBsaW8sIGluY2x1eWVuZG8gbWVkaWEsIGRlc3bDrW8gZXN0w6FuZGFyLCBtZWRpYW5hLCBQMjXigJNQNzUsIElRUiwgbcOtbmltbyB5IG3DoXhpbW8sIGNvbiBlbCBvYmpldGl2byBkZSBkZWZpbmlyIHBvc3Rlcmlvcm1lbnRlIGVsIGZvcm1hdG8gZGUgcmVwb3J0ZSBtw6FzIGFkZWN1YWRvIHBhcmEgbGEgdmVyc2nDs24gZmluYWwgZGVsIG1hbnVzY3JpdG8uIExhcyB2YXJpYWJsZXMgY2F0ZWfDs3JpY2FzIHNlIHByZXNlbnRhbiBtZWRpYW50ZSBmcmVjdWVuY2lhcyBhYnNvbHV0YXMgeSByZWxhdGl2YXMgY2FsY3VsYWRhcyBzb2JyZSBsb3MgZGF0b3MgZGlzcG9uaWJsZXMuDQoNCkxhIFRhYmxhIDIgZGVzY3JpYmUgbG9zIHJlc3VsdGFkb3MgZGUgZnVlcnphIGlzb2NpbsOpdGljYSBzZWfDum4gZ3J1cG8gY29tcGV0aXRpdm8geSB2ZWxvY2lkYWQgZGUgZXZhbHVhY2nDs24uIFBhcmEgdG9kYXMgbGFzIHZhcmlhYmxlcyBkZSBmdWVyemEgc2UgaW5mb3JtYW4gbWVkaWRhcyBkZSB0ZW5kZW5jaWEgY2VudHJhbCB5IGRpc3BlcnNpw7NuIHRhbnRvIHBhcmFtw6l0cmljYXMgY29tbyByb2J1c3RhcywgZXZpdGFuZG8gaW1wb25lciB1bmEgw7puaWNhIGZvcm1hIGRlIHJlc3VtZW4gc29icmUgdW4gY29uanVudG8gYW1wbGlvIHkgaGV0ZXJvZ8OpbmVvIGRlIG3DqXRyaWNhcy4NCg0KTGFzIGZpZ3VyYXMgZGVzY3JpcHRpdmFzIHByb3B1ZXN0YXMgcGVybWl0ZW4gdmlzdWFsaXphciBlbCBjb21wb3J0YW1pZW50byBkZWwgcGljbyBkZSBwYXIgZW4gZXh0ZW5zacOzbiwgZGVsIHBpY28gZGUgcGFyIGVuIGZsZXhpw7NuIHkgZGUgbGFzIHNpbWV0csOtYXMgY29ycmVzcG9uZGllbnRlcyBzZWfDum4gbml2ZWwgY29tcGV0aXRpdm8geSBtb21lbnRvIHBvc3RvcGVyYXRvcmlvLiBTZSBlbGlnacOzIHVuYSByZXByZXNlbnRhY2nDs24gYmFzYWRhIGVuIG1lZGlhbmFzIHkgcmFuZ29zIGludGVyY3VhcnTDrWxpY29zIHBhcmEgb2ZyZWNlciB1bmEgbGVjdHVyYSBtw6FzIGVzdGFibGUgZnJlbnRlIGEgcG9zaWJsZXMgdmFsb3JlcyBleHRyZW1vcy4NCg0KDQpgYGB7cn0NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDI4KSBGSUdVUkFTIEZJTkFMRVMgVElQTyBCT1hQTE9UICsgUFVOVE9TDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyA0IGZpZ3VyYXMgcHJpbmNpcGFsZXM6DQojIDEpIFBpY28gZGVsIHBhciBlbiBleHRlbnNpw7NuIC0gaW1wbGljYWRvDQojIDIpIFBpY28gZGVsIHBhciBlbiBmbGV4acOzbiAtIGltcGxpY2Fkbw0KIyAzKSBTaW1ldHLDrWEgZGVsIHBpY28gZGVsIHBhciBlbiBleHRlbnNpw7NuDQojIDQpIFNpbWV0csOtYSBkZWwgcGljbyBkZWwgcGFyIGVuIGZsZXhpw7NuDQojDQojIENhZGEgZmlndXJhIHNlIGRpdmlkZSBlbiAzIHN1YnBhbmVsZXM6DQojIGEpIDYwwrAvcw0KIyBiKSAxODDCsC9zDQojIGMpIDMwMMKwL3MNCiMNCiMgRWplIFg6IG1vbWVudG8gcG9zdG9wZXJhdG9yaW8NCiMgRWplIFk6IHZhcmlhYmxlIG51bcOpcmljYQ0KIyBDb2xvci9yZWxsZW5vOiBncnVwbyBjb21wZXRpdGl2bw0KIyBQdW50b3M6IGV2YWx1YWNpb25lcyBpbmRpdmlkdWFsZXMNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMjguMSkgQ3JlYXIgY2FycGV0YSBwYXJhIGd1YXJkYXIgbGFzIGZpZ3VyYXMgZmluYWxlcw0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KY2FycGV0YV9maWd1cmFzX2JveHBsb3QgPC0gZmlsZS5wYXRoKA0KICBnZXR3ZCgpLA0KICAiZmlndXJhc19pc29jaW5lc2lhX1JMQ0EiLA0KICAiYm94cGxvdHNfZmluYWxlcyINCikNCg0KZGlyLmNyZWF0ZSgNCiAgY2FycGV0YV9maWd1cmFzX2JveHBsb3QsDQogIHJlY3Vyc2l2ZSA9IFRSVUUsDQogIHNob3dXYXJuaW5ncyA9IEZBTFNFDQopDQoNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMjguMikgRnVuY2nDs24gcGFyYSBwcmVwYXJhciBkYXRvcw0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KcHJlcGFyYXJfZGF0b3NfYm94cGxvdCA8LSBmdW5jdGlvbigNCiAgICBkYXRhLA0KICAgIHZhcmlhYmxlX3ZhbG9yDQopew0KICANCiAgZGF0YSAlPiUNCiAgICBzZWxlY3QoDQogICAgICBpZCwNCiAgICAgIHNlc2lvbiwNCiAgICAgIHZlbG9jaWRhZCwNCiAgICAgIGdydXBvX2NvbXBldGVuY2lhLA0KICAgICAgY2F0ZWdvcmlhX21vbWVudG9fcG9wX2Rlcml2YWRhLA0KICAgICAgYWxsX29mKHZhcmlhYmxlX3ZhbG9yKQ0KICAgICkgJT4lDQogICAgcmVuYW1lKA0KICAgICAgdmFsb3IgPSBhbGxfb2YodmFyaWFibGVfdmFsb3IpDQogICAgKSAlPiUNCiAgICBmaWx0ZXIoDQogICAgICAhaXMubmEodmFsb3IpLA0KICAgICAgIWlzLm5hKHZlbG9jaWRhZCksDQogICAgICAhaXMubmEoZ3J1cG9fY29tcGV0ZW5jaWEpLA0KICAgICAgIWlzLm5hKGNhdGVnb3JpYV9tb21lbnRvX3BvcF9kZXJpdmFkYSkNCiAgICApICU+JQ0KICAgIG11dGF0ZSgNCiAgICAgIHZlbG9jaWRhZF9udW0gPSByZWFkcjo6cGFyc2VfbnVtYmVyKGFzLmNoYXJhY3Rlcih2ZWxvY2lkYWQpKSwNCiAgICAgIA0KICAgICAgdmVsb2NpZGFkX2V0aXF1ZXRhID0gY2FzZV93aGVuKA0KICAgICAgICB2ZWxvY2lkYWRfbnVtID09IDYwIH4gImEpIDYwwrAvcyIsDQogICAgICAgIHZlbG9jaWRhZF9udW0gPT0gMTgwIH4gImIpIDE4MMKwL3MiLA0KICAgICAgICB2ZWxvY2lkYWRfbnVtID09IDMwMCB+ICJjKSAzMDDCsC9zIiwNCiAgICAgICAgVFJVRSB+IE5BX2NoYXJhY3Rlcl8NCiAgICAgICksDQogICAgICANCiAgICAgIHZlbG9jaWRhZF9ldGlxdWV0YSA9IGZhY3RvcigNCiAgICAgICAgdmVsb2NpZGFkX2V0aXF1ZXRhLA0KICAgICAgICBsZXZlbHMgPSBjKCJhKSA2MMKwL3MiLCAiYikgMTgwwrAvcyIsICJjKSAzMDDCsC9zIikNCiAgICAgICksDQogICAgICANCiAgICAgIGNhdGVnb3JpYV9tb21lbnRvX3BvcF9kZXJpdmFkYSA9IGZhY3RvcigNCiAgICAgICAgY2F0ZWdvcmlhX21vbWVudG9fcG9wX2Rlcml2YWRhLA0KICAgICAgICBsZXZlbHMgPSBjKCI8NiBtZXNlcyIsICI2IGEgPDggbWVzZXMiLCAi4omlOCBtZXNlcyIpDQogICAgICApLA0KICAgICAgDQogICAgICBncnVwb19jb21wZXRlbmNpYSA9IGZhY3RvcigNCiAgICAgICAgZ3J1cG9fY29tcGV0ZW5jaWEsDQogICAgICAgIGxldmVscyA9IGMoIlByaW1lcmEiLCAiU2VndW5kYSIsICJUZXJjZXJhICsgQW1hdGV1ciIpDQogICAgICApDQogICAgKSAlPiUNCiAgICBmaWx0ZXIoIWlzLm5hKHZlbG9jaWRhZF9ldGlxdWV0YSkpDQp9DQoNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMjguMykgRnVuY2nDs24gcGFyYSB0YWJsYSBkZSBuIHBvciBjZWxkYQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KY3JlYXJfdGFibGFfbl9maWd1cmEgPC0gZnVuY3Rpb24oDQogICAgZGF0YV9wbG90LA0KICAgIG5vbWJyZV9maWd1cmENCil7DQogIA0KICBkYXRhX3Bsb3QgJT4lDQogICAgZ3JvdXBfYnkoDQogICAgICB2ZWxvY2lkYWRfZXRpcXVldGEsDQogICAgICBjYXRlZ29yaWFfbW9tZW50b19wb3BfZGVyaXZhZGEsDQogICAgICBncnVwb19jb21wZXRlbmNpYQ0KICAgICkgJT4lDQogICAgc3VtbWFyaXNlKA0KICAgICAgbl9ldmFsdWFjaW9uZXMgPSBuX2Rpc3RpbmN0KHBhc3RlKGlkLCBzZXNpb24pKSwNCiAgICAgIG5fanVnYWRvcmVzID0gbl9kaXN0aW5jdChpZCksDQogICAgICAuZ3JvdXBzID0gImRyb3AiDQogICAgKSAlPiUNCiAgICBtdXRhdGUoDQogICAgICBmaWd1cmEgPSBub21icmVfZmlndXJhLA0KICAgICAgYmFqb19uID0gY2FzZV93aGVuKA0KICAgICAgICBuX2V2YWx1YWNpb25lcyA8IDUgfiAiU8OtIiwNCiAgICAgICAgVFJVRSB+ICJObyINCiAgICAgICkNCiAgICApICU+JQ0KICAgIHJlbG9jYXRlKA0KICAgICAgZmlndXJhLA0KICAgICAgdmVsb2NpZGFkX2V0aXF1ZXRhLA0KICAgICAgY2F0ZWdvcmlhX21vbWVudG9fcG9wX2Rlcml2YWRhLA0KICAgICAgZ3J1cG9fY29tcGV0ZW5jaWENCiAgICApDQp9DQoNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMjguNCkgRnVuY2nDs24gcGFyYSBwb3NpY2nDs24gZGUgZXRpcXVldGFzIG4NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNCmNyZWFyX3Bvc2ljaW9uX24gPC0gZnVuY3Rpb24oZGF0YV9wbG90KXsNCiAgDQogIHJhbmdvX3Bvcl92ZWxvY2lkYWQgPC0gZGF0YV9wbG90ICU+JQ0KICAgIGdyb3VwX2J5KHZlbG9jaWRhZF9ldGlxdWV0YSkgJT4lDQogICAgc3VtbWFyaXNlKA0KICAgICAgeV9taW4gPSBtaW4odmFsb3IsIG5hLnJtID0gVFJVRSksDQogICAgICB5X21heCA9IG1heCh2YWxvciwgbmEucm0gPSBUUlVFKSwNCiAgICAgIHJhbmdvID0geV9tYXggLSB5X21pbiwNCiAgICAgIC5ncm91cHMgPSAiZHJvcCINCiAgICApICU+JQ0KICAgIG11dGF0ZSgNCiAgICAgIHlfbiA9IHlfbWF4ICsgMC4wOCAqIHJhbmdvDQogICAgKQ0KICANCiAgZGF0YV9wbG90ICU+JQ0KICAgIGdyb3VwX2J5KA0KICAgICAgdmVsb2NpZGFkX2V0aXF1ZXRhLA0KICAgICAgY2F0ZWdvcmlhX21vbWVudG9fcG9wX2Rlcml2YWRhLA0KICAgICAgZ3J1cG9fY29tcGV0ZW5jaWENCiAgICApICU+JQ0KICAgIHN1bW1hcmlzZSgNCiAgICAgIG5fZXZhbHVhY2lvbmVzID0gbl9kaXN0aW5jdChwYXN0ZShpZCwgc2VzaW9uKSksDQogICAgICBuX2p1Z2Fkb3JlcyA9IG5fZGlzdGluY3QoaWQpLA0KICAgICAgLmdyb3VwcyA9ICJkcm9wIg0KICAgICkgJT4lDQogICAgbGVmdF9qb2luKA0KICAgICAgcmFuZ29fcG9yX3ZlbG9jaWRhZCwNCiAgICAgIGJ5ID0gInZlbG9jaWRhZF9ldGlxdWV0YSINCiAgICApICU+JQ0KICAgIG11dGF0ZSgNCiAgICAgIGV0aXF1ZXRhX24gPSBwYXN0ZTAoIm49Iiwgbl9ldmFsdWFjaW9uZXMpDQogICAgKQ0KfQ0KDQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDI4LjUpIEZ1bmNpw7NuIHBhcmEgY3JlYXIgYm94cGxvdCBmaW5hbA0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KY3JlYXJfYm94cGxvdF9maW5hbCA8LSBmdW5jdGlvbigNCiAgICBkYXRhX3Bsb3QsDQogICAgdGl0dWxvLA0KICAgIHN1YnRpdHVsbywNCiAgICBldGlxdWV0YV95LA0KICAgIGFncmVnYXJfbGluZWFfMTAwID0gRkFMU0UsDQogICAgbW9zdHJhcl9uID0gRkFMU0UNCil7DQogIA0KICBkYXRvc19uIDwtIGNyZWFyX3Bvc2ljaW9uX24oZGF0YV9wbG90KQ0KICANCiAgZ3JhZmljbyA8LSBnZ3Bsb3QoDQogICAgZGF0YV9wbG90LA0KICAgIGFlcygNCiAgICAgIHggPSBjYXRlZ29yaWFfbW9tZW50b19wb3BfZGVyaXZhZGEsDQogICAgICB5ID0gdmFsb3IsDQogICAgICBmaWxsID0gZ3J1cG9fY29tcGV0ZW5jaWEsDQogICAgICBjb2xvciA9IGdydXBvX2NvbXBldGVuY2lhDQogICAgKQ0KICApDQogIA0KICBpZihhZ3JlZ2FyX2xpbmVhXzEwMCl7DQogICAgZ3JhZmljbyA8LSBncmFmaWNvICsNCiAgICAgIGdlb21faGxpbmUoDQogICAgICAgIHlpbnRlcmNlcHQgPSAxMDAsDQogICAgICAgIGxpbmV0eXBlID0gImRhc2hlZCIsDQogICAgICAgIGNvbG9yID0gImdyZXkzNSIsDQogICAgICAgIGxpbmV3aWR0aCA9IDAuNQ0KICAgICAgKQ0KICB9DQogIA0KICBncmFmaWNvIDwtIGdyYWZpY28gKw0KICAgIGdlb21fYm94cGxvdCgNCiAgICAgIHdpZHRoID0gMC41OCwNCiAgICAgIGFscGhhID0gMC4zMCwNCiAgICAgIG91dGxpZXIuc2hhcGUgPSBOQSwNCiAgICAgIGxpbmV3aWR0aCA9IDAuNTAsDQogICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC43NSkNCiAgICApICsNCiAgICBnZW9tX3BvaW50KA0KICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9qaXR0ZXJkb2RnZSgNCiAgICAgICAgaml0dGVyLndpZHRoID0gMC4xMCwNCiAgICAgICAgaml0dGVyLmhlaWdodCA9IDAsDQogICAgICAgIGRvZGdlLndpZHRoID0gMC43NSwNCiAgICAgICAgc2VlZCA9IDEyMw0KICAgICAgKSwNCiAgICAgIHNpemUgPSAxLjcsDQogICAgICBhbHBoYSA9IDAuNjUsDQogICAgICBzdHJva2UgPSAwLjIwDQogICAgKQ0KICANCiAgaWYobW9zdHJhcl9uKXsNCiAgICANCiAgICBncmFmaWNvIDwtIGdyYWZpY28gKw0KICAgICAgZ2VvbV90ZXh0KA0KICAgICAgICBkYXRhID0gZGF0b3NfbiwNCiAgICAgICAgYWVzKA0KICAgICAgICAgIHggPSBjYXRlZ29yaWFfbW9tZW50b19wb3BfZGVyaXZhZGEsDQogICAgICAgICAgeSA9IHlfbiwNCiAgICAgICAgICBsYWJlbCA9IGV0aXF1ZXRhX24sDQogICAgICAgICAgY29sb3IgPSBncnVwb19jb21wZXRlbmNpYSwNCiAgICAgICAgICBncm91cCA9IGdydXBvX2NvbXBldGVuY2lhDQogICAgICAgICksDQogICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjc1KSwNCiAgICAgICAgaW5oZXJpdC5hZXMgPSBGQUxTRSwNCiAgICAgICAgc2l6ZSA9IDMuMCwNCiAgICAgICAgc2hvdy5sZWdlbmQgPSBGQUxTRQ0KICAgICAgKQ0KICB9DQogIA0KICBncmFmaWNvICsNCiAgICBmYWNldF93cmFwKA0KICAgICAgfiB2ZWxvY2lkYWRfZXRpcXVldGEsDQogICAgICBucm93ID0gMQ0KICAgICkgKw0KICAgIHNjYWxlX3lfY29udGludW91cygNCiAgICAgIGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gYygwLjA0LCAwLjE1KSkNCiAgICApICsNCiAgICBsYWJzKA0KICAgICAgdGl0bGUgPSB0aXR1bG8sDQogICAgICBzdWJ0aXRsZSA9IHN1YnRpdHVsbywNCiAgICAgIHggPSAiTW9tZW50byBwb3N0b3BlcmF0b3JpbyIsDQogICAgICB5ID0gZXRpcXVldGFfeSwNCiAgICAgIGZpbGwgPSAiR3J1cG8gY29tcGV0aXRpdm8iLA0KICAgICAgY29sb3IgPSAiR3J1cG8gY29tcGV0aXRpdm8iLA0KICAgICAgY2FwdGlvbiA9ICJDYWRhIHB1bnRvIHJlcHJlc2VudGEgdW5hIGV2YWx1YWNpw7NuIGNsw61uaWNhIMO6bmljYS4gVW4gbWlzbW8ganVnYWRvciBwdWRvIGFwb3J0YXIgbcOhcyBkZSB1bmEgZXZhbHVhY2nDs24gc2kgZnVlIG1lZGlkbyBlbiBkaXN0aW50b3MgbW9tZW50b3MgcG9zdG9wZXJhdG9yaW9zLiBMYXMgY2VsZGFzIGNvbiBiYWpvIG7Dum1lcm8gZGUgZXZhbHVhY2lvbmVzIGRlYmVuIGludGVycHJldGFyc2UgY29uIGNhdXRlbGEuIg0KICAgICkgKw0KICAgIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTEpICsNCiAgICB0aGVtZSgNCiAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLA0KICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiksDQogICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDIwLCBoanVzdCA9IDEpLA0KICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiKSwNCiAgICAgIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dCgNCiAgICAgICAgaGp1c3QgPSAwLA0KICAgICAgICBzaXplID0gOCwNCiAgICAgICAgY29sb3IgPSAiZ3JleTMwIg0KICAgICAgKQ0KICAgICkNCn0NCg0KDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAyOC42KSBGSUdVUkEgMQ0KIyBQaWNvIGRlbCBwYXIgZW4gZXh0ZW5zacOzbiAtIGltcGxpY2Fkbw0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KZGF0b3NfZmlndXJhXzEgPC0gcHJlcGFyYXJfZGF0b3NfYm94cGxvdCgNCiAgZGF0YSA9IGRhdG9zX2FuYWxpc2lzLA0KICB2YXJpYWJsZV92YWxvciA9ICJwaWNvX2RlbF9wYXJfZXh0ZW5zaW9uX2ltcGxpY2FkbyINCikNCg0KZmlndXJhXzFfYm94cGxvdCA8LSBjcmVhcl9ib3hwbG90X2ZpbmFsKA0KICBkYXRhX3Bsb3QgPSBkYXRvc19maWd1cmFfMSwNCiAgdGl0dWxvID0gIkZpZ3VyYSAxLiBQaWNvIGRlbCBwYXIgZW4gZXh0ZW5zacOzbiBkZWwgbWllbWJybyBpbXBsaWNhZG8iLA0KICBzdWJ0aXR1bG8gPSAiQm94cGxvdCBjb24gcHVudG9zIGluZGl2aWR1YWxlcyBwb3IgbW9tZW50byBwb3N0b3BlcmF0b3JpbyB5IGdydXBvIGNvbXBldGl0aXZvLiIsDQogIGV0aXF1ZXRhX3kgPSAiUGljbyBkZWwgcGFyIGVuIGV4dGVuc2nDs24gKE7Ct20pIiwNCiAgYWdyZWdhcl9saW5lYV8xMDAgPSBGQUxTRSwNCiAgbW9zdHJhcl9uID0gRkFMU0UNCikNCg0KZmlndXJhXzFfYm94cGxvdA0KDQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDI4LjcpIEZJR1VSQSAyDQojIFBpY28gZGVsIHBhciBlbiBmbGV4acOzbiAtIGltcGxpY2Fkbw0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KZGF0b3NfZmlndXJhXzIgPC0gcHJlcGFyYXJfZGF0b3NfYm94cGxvdCgNCiAgZGF0YSA9IGRhdG9zX2FuYWxpc2lzLA0KICB2YXJpYWJsZV92YWxvciA9ICJwaWNvX2RlbF9wYXJfZmxleGlvbl9pbXBsaWNhZG8iDQopDQoNCmZpZ3VyYV8yX2JveHBsb3QgPC0gY3JlYXJfYm94cGxvdF9maW5hbCgNCiAgZGF0YV9wbG90ID0gZGF0b3NfZmlndXJhXzIsDQogIHRpdHVsbyA9ICJGaWd1cmEgMi4gUGljbyBkZWwgcGFyIGVuIGZsZXhpw7NuIGRlbCBtaWVtYnJvIGltcGxpY2FkbyIsDQogIHN1YnRpdHVsbyA9ICJCb3hwbG90IGNvbiBwdW50b3MgaW5kaXZpZHVhbGVzIHBvciBtb21lbnRvIHBvc3RvcGVyYXRvcmlvIHkgZ3J1cG8gY29tcGV0aXRpdm8uIiwNCiAgZXRpcXVldGFfeSA9ICJQaWNvIGRlbCBwYXIgZW4gZmxleGnDs24gKE7Ct20pIiwNCiAgYWdyZWdhcl9saW5lYV8xMDAgPSBGQUxTRSwNCiAgbW9zdHJhcl9uID0gRkFMU0UNCikNCg0KZmlndXJhXzJfYm94cGxvdA0KDQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDI4LjgpIEZJR1VSQSAzDQojIFNpbWV0csOtYSBkZWwgcGljbyBkZWwgcGFyIGVuIGV4dGVuc2nDs24NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNCmRhdG9zX2ZpZ3VyYV8zIDwtIHByZXBhcmFyX2RhdG9zX2JveHBsb3QoDQogIGRhdGEgPSBkYXRvc19hbmFsaXNpcywNCiAgdmFyaWFibGVfdmFsb3IgPSAic2ltZXRyaWFfcGljb19wYXJfZXh0ZW5zaW9uIg0KKQ0KDQpmaWd1cmFfM19ib3hwbG90IDwtIGNyZWFyX2JveHBsb3RfZmluYWwoDQogIGRhdGFfcGxvdCA9IGRhdG9zX2ZpZ3VyYV8zLA0KICB0aXR1bG8gPSAiRmlndXJhIDMuIFNpbWV0csOtYSBkZWwgcGljbyBkZWwgcGFyIGVuIGV4dGVuc2nDs24iLA0KICBzdWJ0aXR1bG8gPSAiQm94cGxvdCBjb24gcHVudG9zIGluZGl2aWR1YWxlcyBwb3IgbW9tZW50byBwb3N0b3BlcmF0b3JpbyB5IGdydXBvIGNvbXBldGl0aXZvLiBMYSBsw61uZWEgZGlzY29udGludWEgaW5kaWNhIDEwMCUgZGUgc2ltZXRyw61hLiIsDQogIGV0aXF1ZXRhX3kgPSAiU2ltZXRyw61hIGRlbCBwaWNvIGRlbCBwYXIgZW4gZXh0ZW5zacOzbiAoJSkiLA0KICBhZ3JlZ2FyX2xpbmVhXzEwMCA9IFRSVUUsDQogIG1vc3RyYXJfbiA9IEZBTFNFDQopDQoNCmZpZ3VyYV8zX2JveHBsb3QNCg0KDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAyOC45KSBGSUdVUkEgNA0KIyBTaW1ldHLDrWEgZGVsIHBpY28gZGVsIHBhciBlbiBmbGV4acOzbg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KZGF0b3NfZmlndXJhXzQgPC0gcHJlcGFyYXJfZGF0b3NfYm94cGxvdCgNCiAgZGF0YSA9IGRhdG9zX2FuYWxpc2lzLA0KICB2YXJpYWJsZV92YWxvciA9ICJzaW1ldHJpYV9waWNvX3Bhcl9mbGV4aW9uIg0KKQ0KDQpmaWd1cmFfNF9ib3hwbG90IDwtIGNyZWFyX2JveHBsb3RfZmluYWwoDQogIGRhdGFfcGxvdCA9IGRhdG9zX2ZpZ3VyYV80LA0KICB0aXR1bG8gPSAiRmlndXJhIDQuIFNpbWV0csOtYSBkZWwgcGljbyBkZWwgcGFyIGVuIGZsZXhpw7NuIiwNCiAgc3VidGl0dWxvID0gIkJveHBsb3QgY29uIHB1bnRvcyBpbmRpdmlkdWFsZXMgcG9yIG1vbWVudG8gcG9zdG9wZXJhdG9yaW8geSBncnVwbyBjb21wZXRpdGl2by4gTGEgbMOtbmVhIGRpc2NvbnRpbnVhIGluZGljYSAxMDAlIGRlIHNpbWV0csOtYS4iLA0KICBldGlxdWV0YV95ID0gIlNpbWV0csOtYSBkZWwgcGljbyBkZWwgcGFyIGVuIGZsZXhpw7NuICglKSIsDQogIGFncmVnYXJfbGluZWFfMTAwID0gVFJVRSwNCiAgbW9zdHJhcl9uID0gRkFMU0UNCikNCg0KZmlndXJhXzRfYm94cGxvdA0KDQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojIDI4LjEwKSBUYWJsYSBkZSBuIHBvciBmaWd1cmEgeSBlc3RyYXRvDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KDQp0YWJsYV9uX2ZpZ3VyYXMgPC0gYmluZF9yb3dzKA0KICBjcmVhcl90YWJsYV9uX2ZpZ3VyYSgNCiAgICBkYXRvc19maWd1cmFfMSwNCiAgICAiRmlndXJhIDEuIFBpY28gZGVsIHBhciBlbiBleHRlbnNpw7NuIGRlbCBtaWVtYnJvIGltcGxpY2FkbyINCiAgKSwNCiAgY3JlYXJfdGFibGFfbl9maWd1cmEoDQogICAgZGF0b3NfZmlndXJhXzIsDQogICAgIkZpZ3VyYSAyLiBQaWNvIGRlbCBwYXIgZW4gZmxleGnDs24gZGVsIG1pZW1icm8gaW1wbGljYWRvIg0KICApLA0KICBjcmVhcl90YWJsYV9uX2ZpZ3VyYSgNCiAgICBkYXRvc19maWd1cmFfMywNCiAgICAiRmlndXJhIDMuIFNpbWV0csOtYSBkZWwgcGljbyBkZWwgcGFyIGVuIGV4dGVuc2nDs24iDQogICksDQogIGNyZWFyX3RhYmxhX25fZmlndXJhKA0KICAgIGRhdG9zX2ZpZ3VyYV80LA0KICAgICJGaWd1cmEgNC4gU2ltZXRyw61hIGRlbCBwaWNvIGRlbCBwYXIgZW4gZmxleGnDs24iDQogICkNCikgJT4lDQogIGFycmFuZ2UoDQogICAgZmlndXJhLA0KICAgIHZlbG9jaWRhZF9ldGlxdWV0YSwNCiAgICBjYXRlZ29yaWFfbW9tZW50b19wb3BfZGVyaXZhZGEsDQogICAgZ3J1cG9fY29tcGV0ZW5jaWENCiAgKQ0KDQp0YWJsYV9uX2ZpZ3VyYXMNCg0KDQojIFJlc3VtZW4gZGUgY2VsZGFzIGNvbiBiYWpvIG4NCnRhYmxhX25fZmlndXJhcyAlPiUNCiAgY291bnQoDQogICAgZmlndXJhLA0KICAgIGJham9fbiwNCiAgICBuYW1lID0gIm5fY2VsZGFzIg0KICApDQoNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgMjguMTEpIEd1YXJkYXIgZmlndXJhcyB5IHRhYmxhIGRlIG4NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNCmdnc2F2ZSgNCiAgZmlsZW5hbWUgPSBmaWxlLnBhdGgoDQogICAgY2FycGV0YV9maWd1cmFzX2JveHBsb3QsDQogICAgIkZpZ3VyYV8xX3BpY29fcGFyX2V4dGVuc2lvbl9pbXBsaWNhZG9fYm94cGxvdC5wbmciDQogICksDQogIHBsb3QgPSBmaWd1cmFfMV9ib3hwbG90LA0KICB3aWR0aCA9IDE1LA0KICBoZWlnaHQgPSA4LA0KICBkcGkgPSAzMDANCikNCg0KZ2dzYXZlKA0KICBmaWxlbmFtZSA9IGZpbGUucGF0aCgNCiAgICBjYXJwZXRhX2ZpZ3VyYXNfYm94cGxvdCwNCiAgICAiRmlndXJhXzJfcGljb19wYXJfZmxleGlvbl9pbXBsaWNhZG9fYm94cGxvdC5wbmciDQogICksDQogIHBsb3QgPSBmaWd1cmFfMl9ib3hwbG90LA0KICB3aWR0aCA9IDE1LA0KICBoZWlnaHQgPSA4LA0KICBkcGkgPSAzMDANCikNCg0KZ2dzYXZlKA0KICBmaWxlbmFtZSA9IGZpbGUucGF0aCgNCiAgICBjYXJwZXRhX2ZpZ3VyYXNfYm94cGxvdCwNCiAgICAiRmlndXJhXzNfc2ltZXRyaWFfcGljb19wYXJfZXh0ZW5zaW9uX2JveHBsb3QucG5nIg0KICApLA0KICBwbG90ID0gZmlndXJhXzNfYm94cGxvdCwNCiAgd2lkdGggPSAxNSwNCiAgaGVpZ2h0ID0gOCwNCiAgZHBpID0gMzAwDQopDQoNCmdnc2F2ZSgNCiAgZmlsZW5hbWUgPSBmaWxlLnBhdGgoDQogICAgY2FycGV0YV9maWd1cmFzX2JveHBsb3QsDQogICAgIkZpZ3VyYV80X3NpbWV0cmlhX3BpY29fcGFyX2ZsZXhpb25fYm94cGxvdC5wbmciDQogICksDQogIHBsb3QgPSBmaWd1cmFfNF9ib3hwbG90LA0KICB3aWR0aCA9IDE1LA0KICBoZWlnaHQgPSA4LA0KICBkcGkgPSAzMDANCikNCg0KDQojIEd1YXJkYXIgdGFibGEgZGUgbiBwb3IgZXN0cmF0bw0KaWYoIXJlcXVpcmVOYW1lc3BhY2UoIndyaXRleGwiLCBxdWlldGx5ID0gVFJVRSkpew0KICBpbnN0YWxsLnBhY2thZ2VzKCJ3cml0ZXhsIikNCn0NCg0Kd3JpdGV4bDo6d3JpdGVfeGxzeCgNCiAgdGFibGFfbl9maWd1cmFzLA0KICBwYXRoID0gZmlsZS5wYXRoKA0KICAgIGNhcnBldGFfZmlndXJhc19ib3hwbG90LA0KICAgICJUYWJsYV9uX3Bvcl9maWd1cmFfeV9lc3RyYXRvLnhsc3giDQogICkNCikNCg0KY2FycGV0YV9maWd1cmFzX2JveHBsb3QNCmBgYA0KDQo=