Facultad de Ciencias Físicas y Matemáticas, Universidad de Chile

1. Introducción

El cáncer es una enfermedad genética compleja que surge de los efectos combinados de múltiples variaciones genéticas y epigenéticas (Zhang et al. 2016). Estos cambios pueden influir en la vulnerabilidad celular, provocando la transformación de una célula normal a una cancerosa anormal (Dimitrakopoulos and Beerenwinkel 2017). Estudios recientes han determinado que los cánceres son entidades altamente mutadas, donde los genes afectados están implicados principalmente en la proliferación, regeneración y apoptosis celular, impulsando el crecimiento maligno dentro de los tumores (Kou et al. 2016).
Con el advenimiento de Next Generation Sequencing (NGS) y el desarrollo de la Genómica se ha revolucionado el entendimiento de la biología del cáncer, mejorando el diagnóstico y las terapias, dando paso a la era de la medicina de precisión y la genómica del cáncer (Garraway and Lander 2013).
La medicina de precisión es definida como un enfoque clínico que busca seleccionar el método terapéutico más adecuado para cada paciente considerando diferentes parámetros clínicos y biomarcadores en amplios campos clínicos, como por ejemplo enfermedades metabólicas y cardiovasculares. Ahora, debido a la incipiente investigación de la genómica del cáncer, la oncología clínica será uno de los campos más beneficiados por este nuevo enfoque de investigación que integra el estudio del genoma (Kou et al. 2016). Específicamente, la genómica del cáncer involucra estudios sistemáticos del genoma humano para la identificación de alteraciones genéticas recurrentes en tipos específicos de cáncer. Estas alteraciones genéticas consisten en Single Nucleotide Variants (SNVs), pequeñas inserciones y deleciones (Indels), fusión de genes, Copy-Number Variations (CNVs) y grandes re-arreglos cromosomales, también llamados Structural Variants (Cheng, Zhao, and Zhao 2016).
Actualmente, los investigadores se han enfocado en la identificación de mutaciones “Drivers”, en genes asociados fuertemente al cáncer, que confieren una ventaja proliferativa selectiva a la célula con cáncer con respecto a células normales. Este tipo de mutación estaría causalmente implicada en la oncogénesis, siendo los principales blancos para el desarrollo de nuevas terapias (Nik-Zainal 2014).
Mediante estos estudios genómicos se ha podido determinar que el cáncer no es sólo complejo, sino que también es altamente heterogéneo, debido a que los mecanismos genéticos pueden variar entre pacientes del mismo tipo patológico (Yang et al. 2014). De esta forma, se ha podido identificar variantes genéticas predisponentes y subtipos tumorales, basándose en la firma molecular heterogénea de cada paciente (Riazalhosseini and Lathrop 2016).

2. Problemática

La discriminación de mutaciones “Drivers” de las mutaciones aleatorias o “Passenger” que no desempeñan un papel significativo en el desarrollo del cáncer es actualmente un desafío. Así mismo, éstas y otras mutaciones presentes en un paciente podrían estar asociadas con el diagnóstico y/o pronóstico del cáncer, confiriéndoles un valor predictivo independientemente de la histología del tumor.

3. Hipótesis

Los patrones de mutaciones en los genes Drivers del cáncer y su relación con la anotación de casos clínicos pueden ser utilizados para diseñar métodos predictivos de supervivencia de pacientes con cáncer.

4. Avances y desafíos en la genómica del cáncer

Actualmente, se han realizado varios estudios que buscan caracterizar el escenario genómico para los principales cánceres, como por ejemplo cáncer de pulmón, mama, próstata, estómago entre otros, los cuales tienen una alta incidencia y mortalidad. Esta caracterización genómica consiste principalmente en la identificación de genes driver y pathways, los cuales poseen un rol clave en la generación y desarrollo del cáncer. En el trabajo de (Li et al. 2016) se identificaron los principales genes drivers en Adenocarcinoma de Pulmón (LUAD, Lung Adenocarcinoma) en población asiática. Otro estudio en LUAD se propuso caracterizar el escenario genómico de esta enfermedad mediante la búsqueda de señales de mutación asociadas con la progresión del tumor, logrando identificar genes drivers por medio de la integración de datos epigenómicos, genómicos, la evolución clonal e información de las características clínicas.
En otro reciente estudio, se describió la prevalencia y ubicación de mutaciones somáticas y re-ordenamientos cromosómicos en muestras de tumor en pacientes con cáncer de próstata. Además, se identificaron mutaciones propias de pacientes afroamericanos, reportando una nueva fusión de genes presente en el 17% de los pacientes del estudio (Lindquist et al. 2016).
En la investigación de (Heo et al. 2017) se identificaron mutaciones somáticas en pacientes coreanos con Leucemia Mieloide Aguda (LMA, Acute Myeloid Leukemia), concluyendo que los subtipos morfológicos de la LMA pueden ser reflejados por patrones específicos de alteraciones genómicas. Además, los autores exponen la necesidad de estudios de este tipo, los cuales son muy útiles para el diagnóstico temprano de estas enfermedades complejas.
En el trabajo de (Pereira et al. 2016), no sólo identifican los 40 genes con mutaciones drivers en 2.433 muestras de tumor en cáncer primario de mama, si no que buscaron patrones de asociación entre eventos mutacionales somáticos evaluando el nivel de relación con la sobrevida y patrones clínicos.
Si bien la identificación de genes drivers es un importante avance para lograr comprender la biología del cáncer, determinar patrones de genes mutados que permitan estratificar los pacientes a nivel genómico es el siguiente paso y nuevo desafío.
Recientes estudios han planteado que la clasificación del cáncer en subtipos clínica y biológicamente significativos, puede aumentar la eficiencia de la predicción del diagnóstico y sobrevida, así como también, mejorar la estrategia terapéutica. Sin embargo, este enfoque de investigación es muy reciente, y sólo se ha explorado el genoma de los principales cánceres, dejando una amplia lista de otros tipos de cánceres para su caracterización utilizando su información genómica. Por tal motivo, el presente trabajo tiene por objetivo crear perfiles genómicos de muestras de Adenocarcinoma de Pulmón de The Cancer Genome Atlas (TCGA) para la predicción de sobrevida mediante la integración de anotación clínica y patrones mutacionales.

5. Descripción de los datos.

Los datos con los que trabajaremos corresponden a 230 registros de pacientes de Adenocarcinoma de Pulmón extraídos de Cbioportal for Cancer Genomic. Por cada paciente se tiene la cantidad de mutaciones en cada uno de los genes de su secuencia de ADN: ABL1, AKT1, AKT3, ARAF, AXL, BRAF, CCND1, CDK4, CDK6, etc. Además por cada paciente se registran una serie de datos clínicos: género, edad, tipo de cáncer, estadío del cáncer, estatus como fumador, cuantos meses sobrevivió luego del diagnóstico, entre otros.

#Cargando datos
library(data.table)
setwd("../data")
mutFreq <- fread("luad2014_mut_freq_clinical_data.txt")
caddSco <- fread("luad2014_cadd_score_clinical_data.txt")

A continuación se muestran algunas de las principales características clínicas de los datos utilizados.

library(ggplot2)
require(cowplot)
blank_theme <- theme_minimal() +
  theme(
    axis.title.x = element_blank(),
    axis.title.y = element_blank(),
    panel.border = element_blank(),
    panel.grid=element_blank(),
    axis.ticks = element_blank(),
    axis.text.x=element_blank(),
    plot.title=element_text(size=12, face="bold")
  )
#Gender
data <- setNames(nm = c("Gender", "Freq"), as.data.frame(table(mutFreq$GENDER, useNA="ifany")))
genderPieChart <- ggplot(data, aes(x="", y=Freq, fill=Gender)) +
  geom_bar(width = 1, stat = "identity") +
  coord_polar("y", start=0) +
  blank_theme +
  geom_text(aes(label = Freq), position = position_stack(vjust = 0.5), size=5) +
  ggtitle("Distribución de Género")
#Overall_Survival_Status
y<-table(mutFreq$OS_STATUS, useNA="ifany")
data <- setNames(nm = c("Overall_Survival_Status", "Freq"), as.data.frame(y))
overallSurvPieChart <- ggplot(data, aes(x="", y=Freq, fill=Overall_Survival_Status)) +
  geom_bar(width = 1, stat = "identity") +
  coord_polar("y", start=0) +
  blank_theme +
  geom_text(aes(label = Freq), position = position_stack(vjust = 0.5), size=5) + 
  ggtitle("Estado de Supervivencia")
#AGE
ageHistChart <- ggplot(mutFreq, aes(x=AGE)) +
  geom_histogram(binwidth = 5, na.rm = TRUE) +
  geom_vline(xintercept = mean(mutFreq$AGE, na.rm = TRUE), col="blue") +
  ggtitle("Distribución de Edad")
#Tumor_stage_2009
data <- setNames(nm = c("Tumor_stage_2009", "Freq"), as.data.frame(table(mutFreq$TUMOR_STAGE_2009, useNA="ifany")))
tumorStagePieChart <- ggplot(data, aes(x="", y=Freq, fill=Tumor_stage_2009)) +
  geom_bar(width = 1, stat = "identity") +
  coord_polar("y", start=0) +
  blank_theme +
  geom_text(aes(label = Freq), position = position_stack(vjust = 0.5), size=3) + 
  ggtitle("Estadío del tumor")
plot_grid(plot_grid(genderPieChart, overallSurvPieChart, align='hv'), plot_grid(ageHistChart, tumorStagePieChart), nrow = 2)

#TOBACCO_SMOKING_HISTORY_INDICATOR
y<-table(mutFreq$TOBACCO_SMOKING_HISTORY_INDICATOR, useNA="ifany")
data <- setNames(nm = c("TOBACCO_SMOKING_HISTORY_INDICATOR", "Freq"), as.data.frame(y))
smokingPieChart <- ggplot(data, aes(x="", y=Freq, fill = TOBACCO_SMOKING_HISTORY_INDICATOR)) +
  geom_bar(width = 1, stat = "identity") +
  coord_polar("y", start=0) +
  blank_theme +
  geom_text(aes(label = Freq), position = position_stack(vjust = 0.5), size=3) +
  ggtitle("Estatus de fumador")
#Overall Survival VS TOBACCO_SMOKING_HISTORY_INDICATOR
os <- ecdf(mutFreq$OS_MONTHS)
x <- mutFreq[, c("TOBACCO_SMOKING_HISTORY_INDICATOR", "OS_MONTHS")]
x <- na.omit(x)
library(plyr)
x <- arrange(x, TOBACCO_SMOKING_HISTORY_INDICATOR, OS_MONTHS)
x.ecdf <- ddply(x, .(TOBACCO_SMOKING_HISTORY_INDICATOR), transform, ecdf = ecdf(OS_MONTHS)(OS_MONTHS) )
SurvivalCumBySmokingStepChart <- ggplot() +
  geom_step(data = mutFreq, aes(OS_MONTHS, y = 100 * (1 - os(OS_MONTHS)), colour = "All"), size = 1.2) +
  geom_step(data = x.ecdf, aes(OS_MONTHS, y = 100 * (1 - ecdf), colour = TOBACCO_SMOKING_HISTORY_INDICATOR)) +
  labs(title = "Sobrevivencia", colour = "TOBACCO_SMOKING_HISTORY", x = "Meses Sobrevividos", y = "Sobrevivencia (%)")
plot_grid(smokingPieChart, SurvivalCumBySmokingStepChart, align='hv', nrow = 2)

#Overall Survival VS Gender
x <- mutFreq[, c("GENDER", "OS_MONTHS")]
x <- na.omit(x)
library(plyr)
x <- arrange(x, GENDER, OS_MONTHS)
x.ecdf <- ddply(x, .(GENDER), transform, ecdf = ecdf(OS_MONTHS)(OS_MONTHS) )
SurvivalCumByGenderStepChart <- ggplot() +
  geom_step(data = x.ecdf, aes(OS_MONTHS, y = 100 * (1 - ecdf), colour = GENDER), size = 0.8) +
  labs(title = "Sobrevivencia", colour = "GENDER", x = "Meses Sobrevividos", y = "Sobrevivencia (%)")
SurvivalByGenderAndTumorSubtypeBarChart <- ggplot(aes(y = OS_MONTHS, x = HISTOLOGICAL_SUBTYPE, fill = GENDER), data = na.omit(mutFreq[, c("OS_MONTHS", "HISTOLOGICAL_SUBTYPE", "GENDER")])) +
  geom_bar(stat = "identity", position = "dodge", na.rm = TRUE) +
  coord_flip() +
  theme(axis.text.x = element_text(angle = 50, hjust = 1, size=8), axis.text.y = element_text(size=8), legend.text = element_text(size = 8), legend.title = element_text(size = 10)) +
  xlab("Subtipo de Tumor") +
  ylab("Meses Sobrevividos")
plot_grid(SurvivalCumByGenderStepChart, SurvivalByGenderAndTumorSubtypeBarChart, align='hv', nrow = 2)

SurvivalSmokingGenderBoxplot <- ggplot(aes(y = OS_MONTHS, x = TOBACCO_SMOKING_HISTORY_INDICATOR, fill = GENDER), data = mutFreq) +
  geom_boxplot() +
  theme(axis.text.x = element_text(angle = 50, hjust = 1, size=8)) +
  xlab("Tipo de fumador") +
  ylab("Overall survival")
#SurvivalSmokingOSStatusBoxplot <- ggplot(aes(y = OS_MONTHS, x = TOBACCO_SMOKING_HISTORY_INDICATOR, fill = OS_STATUS), data = mutFreq) +
#  geom_boxplot() +
#  theme(axis.text.x = element_text(angle = 50, hjust = 1, size=8)) +
#  xlab("Tipo de fumador") +
#  ylab("Overall survival")
#plot_grid(SurvivalSmokingGenderBoxplot, SurvivalSmokingOSStatusBoxplot, align='v')
#SurvivalTumorStageGenderBoxplot <- ggplot(aes(y = OS_MONTHS, x = TUMOR_STAGE_2009, fill = GENDER), data = mutFreq) +
#  geom_boxplot() +
#  theme(axis.text.x = element_text(angle = 50, hjust = 1, size=8)) +
#  xlab("Estadio de tumor") +
#  ylab("Overall survival")
#SurvivalTumorStageOSStatusBoxplot <- ggplot(aes(y = OS_MONTHS, x = TUMOR_STAGE_2009, fill = OS_STATUS), data = mutFreq) +
#  geom_boxplot() +
#  theme(axis.text.x = element_text(angle = 50, hjust = 1, size=8)) +
#  xlab("Estadio de tumor") +
#  ylab("Overall survival")
#plot_grid(SurvivalTumorStageGenderBoxplot, SurvivalTumorStageOSStatusBoxplot, align='v')
SurvivalTumorSubtypeGenderBoxplot <- ggplot(aes(y = AGE, x = HISTOLOGICAL_SUBTYPE, fill = GENDER), data = mutFreq) +
  geom_boxplot() +
  theme(axis.text.x = element_text(angle = 50, hjust = 1, size=8)) +
  xlab("tipo de tumor") +
  ylab("Overall survival")
#SurvivalTumorSubtypeOSStatusBoxplot <- ggplot(aes(y = OS_MONTHS, x = HISTOLOGICAL_SUBTYPE, fill = OS_STATUS), data = mutFreq) +
#  geom_boxplot() +
#  theme(axis.text.x = element_text(angle = 50, hjust = 1, size=8)) +
#  xlab("tipo de tumor") +
#  ylab("Overall survival")
plot_grid(SurvivalSmokingGenderBoxplot, SurvivalTumorSubtypeGenderBoxplot, align='v')

En la siguiente tabla se muestra, por cada gen la cantidad total de mutaciones presentes en la data, cantidad de pacientes que presentan mutaciones en este gen y el porciento que representa del total.

#Get total of mutations by gene
genes <- setNames(nm = c("Gene", "Mut"), stack(colSums(mutFreq[, 23:14780]))[2:1])
#Get total of CADD Score by gene
pat <- setNames(nm = c("Gene", "Score"), stack(colSums(caddSco[, 2:14759]))[2:1])
pat <- merge(pat, genes[, c("Gene", "Mut")], by="Gene")
#Add total of patients by mutated gene
pat <- merge(pat, setNames(nm = c("Gene", "Pat"), stack(colSums(caddSco[,  2:14759] / caddSco[,  2:14759], na.rm = TRUE))[2:1]), by="Gene")
pat<- pat[order(pat$Score, decreasing = TRUE), ]
#Add CADD Score of mutated gene by patient
pat$Freq <- apply(data.table(pat[, 4]), 1, function(x){return(x * 100 / 230)})
pat

5.1 Pre-procesamiento

La mayoría de estudios genómicos se han basado en medidas o indicadores que permiten interpretar variantes genómicas y relacionarlas con la generación y progresión del cáncer, por ejemplo, la frecuencia mutacional. En varios estudios esta medida ha sido clave para la identificación de genes drivers. Sin embargo, otras investigaciones proponen utilizar diferentes medidas que correlacionen la funcionalidad molecular como la patogenicidad de las mutaciones en los genes. Estas medidas son conocidas como Measures Deleteriousness.
En el trabajo de (Vural, Wang, and Guda 2016) se utilizó un método para integrar anotación funcional, conservación e información del modelo del gen en un único score llamado el Combined Annotation Dependent Depletion (CADD) score (Kircher et al. 2014), que es una Mesasures Deleteriousness. Este score se correlaciona con la diversidad alélica, anotaciones, patogenicidad de las variantes codificantes, los efectos reguladores medidos experimentalmente y variantes causales de enfermedades dentro del genoma. Este score toma valores en el rango +- infinito, donde un valor alto indica grandes efectos deletéreos de la mutación. En este trabajo se propone crear dos tipos de perfiles basados en la frecuencia mutacional y en una medida de patogenicidad de mutaciones con el objetivo de evaluarlas como medidas predictoras de sobrevida.

5.1.1 Perfil basado en Mutation score

Este tipo de perfil corresponde a una matriz de Mutation Score (n x p), donde n es el número de muestras y p el número de genes. Cada entrada (i, j) contendrá la suma de los CADD score de las mutaciones encontradas para la muestra i en el gen j, esta suma es conocida como Mutation Score. Los Mutation Score serán transformados a una escala de 0 a infinito positivo.

5.2.2 Perfil basado en Fecuencia mutacional

A este perfil corresponde otra matriz con las mismas características estructurales (n x p), donde cada celda (i, j) contendrá la frecuencia relativa de mutaciones para la muestra i en el gen j.

6. Metodología

6.1 Perfiles mutacionales

En cuanto a los datos mutacionales, se crearán perfiles mutacionales basados en el Mutation score, descrito previamente. Se Obtendrá una matriz donde las filas corresponden a las muestras y las columnas a los genes con su correspondiente score de malignidad. Esta matriz será normalizada entre valores de 0 y 1, utilizando el método de máximos y mínimos.

En la siguiente figura se muestra el flujo de trabajo que se realizará para los datos mutacionales.

Flujo de trabajo para datos datos mutacionales

Flujo de trabajo para datos datos mutacionales

6.2 Datos clínicos

En cuando a los datos clínicos, estos pasarán por un proceso de Binarización, un método para codificar variables categóricas, donde cada categoria para cada atributo será convertido en un nuevo atributo con valores binarios (0,1) para denotar la presencia y ausencia de esa categoria para cada muestra. También, los datos númericos, en este caso la edad, será normalizada según el método de máximos y mínimos. Posteriormente, se analizará la distribución de la sobrevida, ya que para los métodos de predicción que se utlizarán se requiere que la variable respuesta, en este caso sobrevida, posea una distribución Normal. Por lo tanto, se evaluará que distribución posee esta variable, se corroborará la necesidad de eliminar outliers, si existen, los cuales pueden tener un efecto negativo en el análisis, y también se verá si es necesario aplicar una transformación (logaritmo) para que la sobrevida tenga una distribución del tipo Normal. En la siguiente figura se muestra el flujo de trabajo que se realizará para los datos clínicos

Flujo de trabajo para datos datos mutacionales

Flujo de trabajo para datos datos mutacionales

luego, las matrices resultantes de los datos mutacionales y clínicos serán unidos por el Id de las muestras, creandose una matriz o perfil clínico-mutacional. Posteriormente, esta matriz será dividida en dos set de datos, el de entrenamiento, con el 75% de los datos y el de test con el 25% restante de forma aleatoria. Los algoritmos que se aplicarán serán entrenados con el set de entrenamiento y luego se aplicarán con el set test. En este trabajo se aplicarán algoritmos de predicción y de clasificación.

Flujo de trabajo para la predicción de la sobrevida

Flujo de trabajo para la predicción de la sobrevida

6.3 Algoritmos de predicción

6.3.1 Principal Component Regression (PCR)

El método Principal Component Regression (PCR), a diferencia de las técnicas de regresión tradicionales que trabajan directamente sobre los datos originales, es capaz de extraer los componentes principales (PCs), que son combinaciones lineales de los features originales, realizando luego una regresión lineal con los coeficientes de los PCs obtenidos. Por lo general, sólo un subconjunto de PCs es elegido para la regresión. La selección tradicional se basa en identificar los PCs con una alta varianza y que estén altamente asociados a la variable respuesta.Se utilizará la función prcomp para calcular los componentes principales (PCs), y la función lm para ajustar el modelo lineal.

6.3.2 Partial Least Squares Regression (PLSR)

Partial Least Squares Regression (PLSR) es una alternativa supervisada a PCR. Al igual que la PCR, PLSR es un método de reducción de dimensiones, que primero identifica un nuevo conjunto más pequeño de características que son combinaciones lineales de las características originales, luego se ajusta un modelo lineal a través de mínimos cuadrados a las nuevas características M. Sin embargo, a diferencia de PCR, PLSR hace uso de la variable de respuesta para identificar las nuevas características. Se usará la función plsr del paquete pls para realizar este análisis.

6.3.3 Regression Tree

Los árboles de predicción se utilizan para predecir una respuesta o clase \(Y\). para las entradas \(X_1, X_2, X_3, ..., X_n\). Si es una respuesta continua, se llama un árbol de regresión, si es categórico, se llama árbol de clasificación. En cada nodo del árbol, se comprueba el valor de una entrada \(X_i\) y dependiendo de la respuesta (binaria) se continúa hacia la sub-rama izquierda o hacia la derecha. Cuando se llega a una hoja, se encuentra la predicción (generalmente es una estadística simple del conjunto de datos que representa la hoja, como el valor más común de las clases disponibles). En este caso, se utlizará un árbol de predicción, ya que la sobrevida es númerica. Para esto se utilizará la función rpart del paquete rpart.

7. Resultados

En cuanto sobrevida, esta presentó una distribución exponencial, por lo tanto se aplicó una transformación logarítmica. También, se encontró dos outliers los cuales fueron removidos del set de datos.

Distribución original de la sobrevida

Distribución original de la sobrevida

Distribución transformada y filtrada de la sobrevida

Distribución transformada y filtrada de la sobrevida

7.2 Principal Component analysis (PCA) con regresión lineal de los PCs significativos

Los datos fueron centralizados y escalados, como requerimiento de PCA. Luego, se calculó PCA usando la función prcomp(). Los primeros 10 componentes fueron los que explican una mayor proporción de la varianza de los datos. Luego, estos 10 PCs se ingresaron como predictores en un modelo de regresión lineal donde la variable respuesta es la sobrevida. El resultado de esto, entregó 5 PCs significativos, PC1, PC2, PC6, PC7 y PC10, los cuales fueron usados como predictores en un nuevo modelo de regresión lineal. Este último modelo fue usado para la predicción con el set set.

Flujo de trabajo para PCA con regresión lineal

Flujo de trabajo para PCA con regresión lineal

Para los resultados de la predicción se calculo el Mean Squares Error, el cual fue de 0.8047338.

7.3 Partial Least Squares Regression (PLSR)

Para determinar el número de componentes que se utilizó en la regresión, se estima el Mean squared error of prediction (MSEP) usando el método de crosvalidación. A continuación, en la siguiente figura se muestra el MSEP para los componentes principales, donde en el 100PC hay un punto de inflección.

MSEP para los PCs

MSEP para los PCs

Posteriormente, se realizó la predicción usando el número de PCs con mejor MSEP y el set test. El MSE de la predicción con el set de datos fue de 6.183472.

7.4 Regression Tree

En la siguiente figura se muestra el modelo de regression tree creado con el set de entrenamiento. Se puede observar que la mayoría de los nodos corresponden a variables clínicas, y que los genes que allí se encuentran no son en su mayoría genes frecuentemente mutados. El valor de MSE de la predicción con el set test fue de 2.447874. MSEP para los PCs

7.5 Algoritmos de Clasificación

La Sobrevida es una variable continua, pero para tratar el problema como un problema de clasificación se hizo necesario transformarla a una variable categórica. Esto se hizo creando 6 clases que cubrieran distintos rangos de tiempo en meses que representan la distribución de la Sobrevida. Se intentó en primer lugar que las clases estuvieran balanceadas, y luego que reflejaran rangos de tiempos representativos para el problema. A continuación se muestra una descripción de esta nueva variable categórica (OS_CLASS).

data <- caddSco
data$OS_CLASS <- as.factor(cut(data$OS_MONTHS, c(0, 1, 6, 12, 24, 36, 300), include.lowest = TRUE))
summary(data$OS_CLASS)
   [0,1]    (1,6]   (6,12]  (12,24]  (24,36] (36,300]     NA's 
      32       37       28       37       29       40       27 
qplot(x = OS_CLASS, data = data)

Para tratar el problema se usaron los siguientes algoritmos de clasificación:

La experimentación se realizó usando solamente los genes “drivers”, es decir los 53 genes que se consideran más fuertemente asociados al desarrollo del cáncer. Para comprobar los resultados obtenidos, se realizó además un test estadístico para ver si los clasificadores eran significativamente diferentes, obteniéndose

# Load libraries
import pandas
from pandas.tools.plotting import scatter_matrix

import matplotlib.pyplot as plt

from sklearn import model_selection
from sklearn.metrics import classification_report
from sklearn.metrics import confusion_matrix
from sklearn.metrics import accuracy_score

from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.naive_bayes import GaussianNB
from sklearn.svm import SVC
from sklearn.dummy import DummyClassifier

from statsmodels.stats.weightstats import ttest_ind


import numpy as np
from sklearn.model_selection import train_test_split

## run_classifier recibe un clasificador, un dataset (X, y) 
## y opcionalmente la cantidad de resultados que se quiere obtener del clasificador
def run_classifier(clf, X, y, num_tests=10):
    scores = []
    
    for _ in range(num_tests):
        ### COMPLETAR ACÁ
        X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25)
        clf.fit(X_train, y_train)
        scores.append( clf.score(X_test, y_test) )  # X_test y y_test deben ser definidos previamente
    
    return np.array(scores)
    


## run_classifiers recibe un dataset (X, y)
def run_classifiers(X, y):
    # Spot Check Algorithms
    models = []
    models.append(('Base', DummyClassifier(strategy='stratified')))
    models.append(('LR', LogisticRegression()))
    #models.append(('LDA', LinearDiscriminantAnalysis()))
    models.append(('KNN', KNeighborsClassifier()))
    models.append(('CART', DecisionTreeClassifier()))
    models.append(('NB', GaussianNB()))
    models.append(('SVM', SVC()))

    result_list = []
    table_head = "NAMES" + "\t" + "Accuracy"
    for name, clf in models:
        table_head += "\t" + name
        accuracys = run_classifier(clf, X, y, 40)
        result_list.append((name, accuracys))

    print("+ indica diferencia significativa\n")
    print(table_head)
    for name1, results1 in result_list:
        table_row = name1 + "\t" + str(results1.mean())
        #print("Comparando %s - Accuracy: %.2f" % (name1, results1.mean()))
        for name2, results2 in result_list:
            table_row = table_row + "\t"
            if name1 == name2:
                continue

            _, p_value, __ = ttest_ind(results1, results2)  # t-test: test estadistico
        
            if p_value <= 0.05:
                sig = "+"
            else:
                sig = ""
            table_row = table_row + sig
            #print("%s:\t%.2f %s" % (name2, results2.mean(), sig))
        print(table_row)




# Load clinical dataset
dataset = pandas.read_csv("../data/clinical_data_bin_with_OS_Class_AGE_NORM.csv")


#Clinical Data
data = dataset.drop(['OS_MONTHS'], axis = 1).drop(['AGE_NORM'], axis = 1)
X = data.values[:, 1:(data.columns.size-1) ]
y = data.values[:, data.columns.size-1]

print("Resultados de Clasificadores con Datos Clínicos\n")
run_classifiers(X, y)
print()
print()



# Load CADD 53 driver genes data
dataset_gen_num_53 = pandas.read_csv("../data/gene_num_53.csv")
#dataset_gen_cadd = pandas.read_csv("../data/gene_cadd_norm.csv")


# CADD 53 Driver Genes Data
#print(dataset.loc[:, ['Row.names', 'OS_CLASS']])
data = pandas.merge(dataset_gen_num_53, dataset.loc[:, ['Row.names', 'OS_CLASS']], on = 'Row.names')
X = data.values[:, 1:(data.columns.size-1)]
y = data.values[:, data.columns.size-1]

print("Resultados de Clasificadores con Datos Genéticos (CADD)\n")
run_classifiers(X, y)
print()
print()


dataset_gen_num_53 = pandas.read_csv("../data/gene_num_53.csv")
#dataset_gen_cadd = pandas.read_csv("../data/gene_cadd_norm.csv")

data = pandas.merge(dataset_gen_num_53, dataset, on = 'Row.names').drop(['OS_MONTHS'], axis = 1).drop(['AGE_NORM'], axis = 1)
X = data.values[:, 1:(data.columns.size-1)]
y = data.values[:, data.columns.size-1]

print("Resultados de Clasificadores con Datos Clínicos y Genéticos\n")
run_classifiers(X, y)
Resultados de Clasificadores con Datos Cl攼㹤nicos

+ indica diferencia significativa

NAMES   Accuracy    Base    LR  KNN CART    NB  SVM
Base    0.166666666667      +       +   +   
LR  0.257352941176  +       +   +   +   +
KNN 0.185294117647      +               
CART    0.196078431373  +   +               +
NB  0.207843137255  +   +               +
SVM 0.166176470588      +       +   +   


Resultados de Clasificadores con Datos Gen攼㸹ticos (CADD)

+ indica diferencia significativa

NAMES   Accuracy    Base    LR  KNN CART    NB  SVM
Base    0.169607843137                      
LR  0.158823529412                      
KNN 0.16862745098                       
CART    0.164215686275                      
NB  0.150980392157                      
SVM 0.167156862745                      


Resultados de Clasificadores con Datos Cl攼㹤nicos y Gen攼㸹ticos

+ indica diferencia significativa

NAMES   Accuracy    Base    LR  KNN CART    NB  SVM
Base    0.169117647059      +       +       +
LR  0.247058823529  +       +   +   +   +
KNN 0.166176470588      +       +       
CART    0.21568627451   +   +   +       +   +
NB  0.157352941176      +       +       
SVM 0.150490196078  +   +       +       

Los resultados obtenidos no son muy buenos, destacándose los clasificadores Logistic Regression y Decision Tree como los de mejores resultados. Se hace necesario un análisis más detallado de las causas de estos resultados.

8. Conclusiones y trabajo futuro

9. Bibliografía

Cheng, Feixiong, Junfei Zhao, and Zhongming Zhao. 2016. “Advances in Computational Approaches for Prioritizing Driver Mutations and Significantly Mutated Genes in Cancer Genomes.” Briefings in Bioinformatics 17 (4): 642-56. doi:10.1093/bib/bbv068.
Dimitrakopoulos, Christos M., and Niko Beerenwinkel. 2017. “Computational Approaches for the Identification of Cancer Genes and Pathways.” Wiley Interdisciplinary Reviews. Systems Biology and Medicine 9 (1). doi:10.1002/wsbm.1364.
Garraway, Levi A., and Eric S. Lander. 2013. “Lessons from the Cancer Genome.” Cell 153 (1): 17-37. doi:10.1016/j.cell.2013.03.002.
Heo, Seong Gu, Youngil Koh, Jong Kwang Kim, Jongsun Jung, Hyung-Lae Kim, Sung-Soo Yoon, and Ji Wan Park. 2017. “Identification of Somatic Mutations Using Whole-Exome Sequencing in Korean Patients with Acute Myeloid Leukemia.” BMC Medical Genetics 18 (March). doi:10.1186/s12881-017-0382-y.
Kircher, Martin, Daniela M Witten, Preti Jain, Brian J O’Roak, Gregory M Cooper, and Jay Shendure. 2014. “A General Framework for Estimating the Relative Pathogenicity of Human Genetic Variants.” Nature Genetics 46 (3): 310-15. doi:10.1038/ng.2892.
Kou, Tadayuki, Masashi Kanai, Shigemi Matsumoto, Yasushi Okuno, and Manabu Muto. 2016. “The Possibility of Clinical Sequencing in the Management of Cancer.” Japanese Journal of Clinical Oncology 46 (5): 399-406. doi:10.1093/jjco/hyw018.
Li, Shiyong, Yoon-La Choi, Zhuolin Gong, Xiao Liu, Maruja Lira, Zhengyan Kan, Ensel Oh, et al. 2016. “Comprehensive Characterization of Oncogenic Drivers in Asian Lung Adenocarcinoma.” Journal of Thoracic Oncology 11 (12): 2129-40. doi:10.1016/j.jtho.2016.08.142.
Lindquist, Karla J., Pamela L. Paris, Thomas J. Hoffmann, Niall J. Cardin, Rémi Kazma, Joel A. Mefford, Jeffrey P. Simko, et al. 2016. “Mutational Landscape of Aggressive Prostate Tumors in African American Men.” Cancer Research 76 (7): 1860-68. doi:10.1158/0008-5472.CAN-15-1787.
Nik-Zainal, Serena. 2014. “Insights into Cancer Biology through next-Generation Sequencing.” Clinical Medicine 14 (Suppl 6): s71-77. doi:10.7861/clinmedicine.14-6-s71.
Pereira, Bernard, Suet-Feung Chin, Oscar M. Rueda, Hans-Kristian Moen Vollan, Elena Provenzano, Helen A. Bardwell, Michelle Pugh, et al. 2016. “The Somatic Mutation Profiles of 2,433 Breast Cancers Refine Their Genomic and Transcriptomic Landscapes.” Nature Communications 7 (May): 11479. doi:10.1038/ncomms11479.
Riazalhosseini, Yasser, and Mark Lathrop. 2016. “Precision Medicine from the Renal Cancer Genome.” Nature Reviews Nephrology 12. doi:10.1038/nrneph.2016.133.
Vural, Suleyman, Xiaosheng Wang, and Chittibabu Guda. 2016. “Classification of Breast Cancer Patients Using Somatic Mutation Profiles and Machine Learning Approaches.” BMC Systems Biology 10 (Suppl 3). doi:10.1186/s12918-016-0306-z.
Yang, William, Kenji Yoshigoe, Xiang Qin, Jun S Liu, Jack Y Yang, Andrzej Niemierko, Youping Deng, et al. 2014. “Identification of Genes and Pathways Involved in Kidney Renal Clear Cell Carcinoma.” BMC Bioinformatics 15 (Suppl 17): S2. doi:10.1186/1471-2105-15-S17-S2.
Zhang, Fan, Chunyan Ren, Kwun Kit Lau, Zihan Zheng, Geming Lu, Zhengzi Yi, Yongzhong Zhao, et al. 2016. “A Network Medicine Approach to Build a Comprehensive Atlas for the Prognosis of Human Cancer.” Briefings in Bioinformatics 17 (6): 1044-59. doi:10.1093/bib/bbw076.

LS0tDQp0aXRsZTogUHJlZGljY2nDs24gZGUgbGEgc3VwZXJ2aXZlbmNpYSBkZSBBZGVub2NhcmNpbm9tYSBkZSBwdWxtw7NuIG1lZGlhbnRlDQogIGxhIGludGVncmFjacOzbiBkZSBwZXJmaWxlcyBkZSBtdXRhY2nDs24geSBhbm90YWNpw7NuIGNsw61uaWNhDQphdXRob3I6ICJLYXJlbiBPcsOzc3RpY2E7IEF5bcOpIEFyYW5nbzsgRHVzdGluIENvYmFzIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50OiBkZWZhdWx0DQogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQNCi0tLQ0KDQo8aDY+RmFjdWx0YWQgZGUgQ2llbmNpYXMgRsOtc2ljYXMgeSBNYXRlbcOhdGljYXMsIFVuaXZlcnNpZGFkIGRlIENoaWxlPC9oNj4NCg0KPGRpdiBzdHlsZT0idGV4dC1hbGlnbjoganVzdGlmeSI+DQoNCiMjIzEuIEludHJvZHVjY2nDs24NCkVsIGPDoW5jZXIgZXMgdW5hIGVuZmVybWVkYWQgZ2Vuw6l0aWNhIGNvbXBsZWphIHF1ZSBzdXJnZSBkZSBsb3MgZWZlY3RvcyBjb21iaW5hZG9zIGRlIG3Dumx0aXBsZXMgdmFyaWFjaW9uZXMgZ2Vuw6l0aWNhcyB5IGVwaWdlbsOpdGljYXMgKFpoYW5nIGV0IGFsLiAyMDE2KS4gRXN0b3MgY2FtYmlvcyBwdWVkZW4gaW5mbHVpciBlbiBsYSB2dWxuZXJhYmlsaWRhZCBjZWx1bGFyLCBwcm92b2NhbmRvIGxhIHRyYW5zZm9ybWFjacOzbiBkZSB1bmEgY8OpbHVsYSBub3JtYWwgYSB1bmEgY2FuY2Vyb3NhIGFub3JtYWwgKERpbWl0cmFrb3BvdWxvcyBhbmQgQmVlcmVud2lua2VsIDIwMTcpLiBFc3R1ZGlvcyByZWNpZW50ZXMgaGFuIGRldGVybWluYWRvIHF1ZSBsb3MgY8OhbmNlcmVzIHNvbiBlbnRpZGFkZXMgYWx0YW1lbnRlIG11dGFkYXMsIGRvbmRlIGxvcyBnZW5lcyBhZmVjdGFkb3MgZXN0w6FuIGltcGxpY2Fkb3MgcHJpbmNpcGFsbWVudGUgZW4gbGEgcHJvbGlmZXJhY2nDs24sIHJlZ2VuZXJhY2nDs24geSBhcG9wdG9zaXMgY2VsdWxhciwgaW1wdWxzYW5kbyBlbCBjcmVjaW1pZW50byBtYWxpZ25vIGRlbnRybyBkZSBsb3MgdHVtb3JlcyAoS291IGV0IGFsLiAyMDE2KS4gIA0KQ29uIGVsIGFkdmVuaW1pZW50byBkZSBOZXh0IEdlbmVyYXRpb24gU2VxdWVuY2luZyAoTkdTKSB5IGVsIGRlc2Fycm9sbG8gZGUgbGEgR2Vuw7NtaWNhIHNlIGhhIHJldm9sdWNpb25hZG8gZWwgZW50ZW5kaW1pZW50byBkZSBsYSBiaW9sb2fDrWEgZGVsIGPDoW5jZXIsIG1lam9yYW5kbyBlbCBkaWFnbsOzc3RpY28geSBsYXMgdGVyYXBpYXMsIGRhbmRvIHBhc28gYSBsYSBlcmEgZGUgbGEgbWVkaWNpbmEgZGUgcHJlY2lzacOzbiB5IGxhIGdlbsOzbWljYSBkZWwgY8OhbmNlciAoR2FycmF3YXkgYW5kIExhbmRlciAyMDEzKS4gIA0KTGEgbWVkaWNpbmEgZGUgcHJlY2lzacOzbiBlcyBkZWZpbmlkYSBjb21vIHVuIGVuZm9xdWUgY2zDrW5pY28gcXVlIGJ1c2NhIHNlbGVjY2lvbmFyIGVsIG3DqXRvZG8gdGVyYXDDqXV0aWNvIG3DoXMgYWRlY3VhZG8gcGFyYSBjYWRhIHBhY2llbnRlIGNvbnNpZGVyYW5kbyBkaWZlcmVudGVzIHBhcsOhbWV0cm9zIGNsw61uaWNvcyB5IGJpb21hcmNhZG9yZXMgZW4gYW1wbGlvcyBjYW1wb3MgY2zDrW5pY29zLCBjb21vIHBvciBlamVtcGxvIGVuZmVybWVkYWRlcyBtZXRhYsOzbGljYXMgeSBjYXJkaW92YXNjdWxhcmVzLiBBaG9yYSwgZGViaWRvIGEgbGEgaW5jaXBpZW50ZSBpbnZlc3RpZ2FjacOzbiBkZSBsYSBnZW7Ds21pY2EgZGVsIGPDoW5jZXIsIGxhIG9uY29sb2fDrWEgY2zDrW5pY2Egc2Vyw6EgdW5vIGRlIGxvcyBjYW1wb3MgbcOhcyBiZW5lZmljaWFkb3MgcG9yIGVzdGUgbnVldm8gZW5mb3F1ZSBkZSBpbnZlc3RpZ2FjacOzbiAgcXVlIGludGVncmEgZWwgZXN0dWRpbyBkZWwgZ2Vub21hIChLb3UgZXQgYWwuIDIwMTYpLiBFc3BlY8OtZmljYW1lbnRlLCBsYSBnZW7Ds21pY2EgZGVsIGPDoW5jZXIgaW52b2x1Y3JhIGVzdHVkaW9zIHNpc3RlbcOhdGljb3MgZGVsIGdlbm9tYSBodW1hbm8gcGFyYSBsYSBpZGVudGlmaWNhY2nDs24gZGUgYWx0ZXJhY2lvbmVzIGdlbsOpdGljYXMgcmVjdXJyZW50ZXMgZW4gdGlwb3MgZXNwZWPDrWZpY29zIGRlIGPDoW5jZXIuIEVzdGFzIGFsdGVyYWNpb25lcyBnZW7DqXRpY2FzIGNvbnNpc3RlbiBlbiBTaW5nbGUgTnVjbGVvdGlkZSBWYXJpYW50cyAoU05WcyksIHBlcXVlw7FhcyBpbnNlcmNpb25lcyB5IGRlbGVjaW9uZXMgKEluZGVscyksIGZ1c2nDs24gZGUgZ2VuZXMsIENvcHktTnVtYmVyIFZhcmlhdGlvbnMgKENOVnMpIHkgZ3JhbmRlcyByZS1hcnJlZ2xvcyBjcm9tb3NvbWFsZXMsIHRhbWJpw6luIGxsYW1hZG9zIFN0cnVjdHVyYWwgVmFyaWFudHMgKENoZW5nLCBaaGFvLCBhbmQgWmhhbyAyMDE2KS4gIA0KQWN0dWFsbWVudGUsIGxvcyBpbnZlc3RpZ2Fkb3JlcyBzZSBoYW4gZW5mb2NhZG8gZW4gbGEgaWRlbnRpZmljYWNpw7NuIGRlIG11dGFjaW9uZXMgIkRyaXZlcnMiLCBlbiBnZW5lcyBhc29jaWFkb3MgZnVlcnRlbWVudGUgYWwgY8OhbmNlciwgcXVlIGNvbmZpZXJlbiB1bmEgdmVudGFqYSBwcm9saWZlcmF0aXZhIHNlbGVjdGl2YSBhIGxhIGPDqWx1bGEgY29uIGPDoW5jZXIgY29uIHJlc3BlY3RvIGEgY8OpbHVsYXMgbm9ybWFsZXMuIEVzdGUgdGlwbyBkZSBtdXRhY2nDs24gZXN0YXLDrWEgY2F1c2FsbWVudGUgaW1wbGljYWRhIGVuIGxhIG9uY29nw6luZXNpcywgc2llbmRvIGxvcyBwcmluY2lwYWxlcyBibGFuY29zIHBhcmEgZWwgZGVzYXJyb2xsbyBkZSBudWV2YXMgdGVyYXBpYXMgKE5pay1aYWluYWwgMjAxNCkuICANCk1lZGlhbnRlIGVzdG9zIGVzdHVkaW9zIGdlbsOzbWljb3Mgc2UgaGEgcG9kaWRvIGRldGVybWluYXIgcXVlIGVsIGPDoW5jZXIgbm8gZXMgc8OzbG8gY29tcGxlam8sIHNpbm8gcXVlIHRhbWJpw6luIGVzIGFsdGFtZW50ZSBoZXRlcm9nw6luZW8sIGRlYmlkbyBhIHF1ZSBsb3MgbWVjYW5pc21vcyBnZW7DqXRpY29zIHB1ZWRlbiB2YXJpYXIgZW50cmUgcGFjaWVudGVzIGRlbCBtaXNtbyB0aXBvIHBhdG9sw7NnaWNvIChZYW5nIGV0IGFsLiAyMDE0KS4gRGUgZXN0YSBmb3JtYSwgc2UgaGEgcG9kaWRvIGlkZW50aWZpY2FyIHZhcmlhbnRlcyBnZW7DqXRpY2FzIHByZWRpc3BvbmVudGVzIHkgc3VidGlwb3MgdHVtb3JhbGVzLCBiYXPDoW5kb3NlIGVuIGxhIGZpcm1hIG1vbGVjdWxhciBoZXRlcm9nw6luZWEgZGUgY2FkYSBwYWNpZW50ZSAoUmlhemFsaG9zc2VpbmkgYW5kIExhdGhyb3AgMjAxNikuDQoNCiMjIzIuIFByb2JsZW3DoXRpY2ENCkxhIGRpc2NyaW1pbmFjacOzbiBkZSBtdXRhY2lvbmVzICJEcml2ZXJzIiBkZSBsYXMgbXV0YWNpb25lcyBhbGVhdG9yaWFzIG8gIlBhc3NlbmdlciIgcXVlIG5vIGRlc2VtcGXDsWFuIHVuIHBhcGVsIHNpZ25pZmljYXRpdm8gZW4gZWwgZGVzYXJyb2xsbyBkZWwgY8OhbmNlciBlcyBhY3R1YWxtZW50ZSB1biBkZXNhZsOtby4gQXPDrSBtaXNtbywgw6lzdGFzIHkgb3RyYXMgbXV0YWNpb25lcyBwcmVzZW50ZXMgZW4gdW4gcGFjaWVudGUgcG9kcsOtYW4gZXN0YXIgYXNvY2lhZGFzIGNvbiBlbCBkaWFnbsOzc3RpY28geS9vIHByb27Ds3N0aWNvIGRlbCBjw6FuY2VyLCBjb25maXJpw6luZG9sZXMgdW4gdmFsb3IgcHJlZGljdGl2byBpbmRlcGVuZGllbnRlbWVudGUgZGUgbGEgaGlzdG9sb2fDrWEgZGVsIHR1bW9yLiAgDQoNCiMjIzMuIEhpcMOzdGVzaXMNCkxvcyBwYXRyb25lcyBkZSBtdXRhY2lvbmVzIGVuIGxvcyBnZW5lcyBEcml2ZXJzIGRlbCBjw6FuY2VyIHkgc3UgcmVsYWNpw7NuIGNvbiBsYSBhbm90YWNpw7NuIGRlIGNhc29zIGNsw61uaWNvcyBwdWVkZW4gc2VyIHV0aWxpemFkb3MgcGFyYSBkaXNlw7FhciBtw6l0b2RvcyBwcmVkaWN0aXZvcyBkZSBzdXBlcnZpdmVuY2lhIGRlIHBhY2llbnRlcyBjb24gY8OhbmNlci4NCg0KDQojIyM0LiBBdmFuY2VzIHkgZGVzYWbDrW9zIGVuIGxhIGdlbsOzbWljYSBkZWwgY8OhbmNlcg0KQWN0dWFsbWVudGUsIHNlIGhhbiByZWFsaXphZG8gdmFyaW9zIGVzdHVkaW9zIHF1ZSBidXNjYW4gY2FyYWN0ZXJpemFyIGVsIGVzY2VuYXJpbyBnZW7Ds21pY28gcGFyYSBsb3MgcHJpbmNpcGFsZXMgY8OhbmNlcmVzLCBjb21vIHBvciBlamVtcGxvIGPDoW5jZXIgZGUgcHVsbcOzbiwgbWFtYSwgcHLDs3N0YXRhLCBlc3TDs21hZ28gZW50cmUgb3Ryb3MsIGxvcyBjdWFsZXMgdGllbmVuIHVuYSBhbHRhIGluY2lkZW5jaWEgeSBtb3J0YWxpZGFkLiBFc3RhIGNhcmFjdGVyaXphY2nDs24gZ2Vuw7NtaWNhIGNvbnNpc3RlIHByaW5jaXBhbG1lbnRlIGVuIGxhIGlkZW50aWZpY2FjacOzbiBkZSBnZW5lcyBkcml2ZXIgeSBwYXRod2F5cywgbG9zIGN1YWxlcyBwb3NlZW4gdW4gcm9sIGNsYXZlIGVuIGxhIGdlbmVyYWNpw7NuIHkgZGVzYXJyb2xsbyBkZWwgY8OhbmNlci4gRW4gZWwgdHJhYmFqbyBkZSAoTGkgZXQgYWwuIDIwMTYpIHNlIGlkZW50aWZpY2Fyb24gbG9zIHByaW5jaXBhbGVzIGdlbmVzIGRyaXZlcnMgZW4gQWRlbm9jYXJjaW5vbWEgZGUgUHVsbcOzbiAoTFVBRCwgTHVuZyBBZGVub2NhcmNpbm9tYSkgZW4gcG9ibGFjacOzbiBhc2nDoXRpY2EuIE90cm8gZXN0dWRpbyBlbiBMVUFEIHNlIHByb3B1c28gY2FyYWN0ZXJpemFyIGVsIGVzY2VuYXJpbyBnZW7Ds21pY28gZGUgZXN0YSBlbmZlcm1lZGFkIG1lZGlhbnRlIGxhIGLDunNxdWVkYSBkZSBzZcOxYWxlcyBkZSBtdXRhY2nDs24gYXNvY2lhZGFzIGNvbiBsYSBwcm9ncmVzacOzbiBkZWwgdHVtb3IsIGxvZ3JhbmRvIGlkZW50aWZpY2FyIGdlbmVzIGRyaXZlcnMgcG9yIG1lZGlvIGRlIGxhIGludGVncmFjacOzbiBkZSBkYXRvcyBlcGlnZW7Ds21pY29zLCBnZW7Ds21pY29zLCBsYSBldm9sdWNpw7NuIGNsb25hbCBlIGluZm9ybWFjacOzbiBkZSBsYXMgY2FyYWN0ZXLDrXN0aWNhcyBjbMOtbmljYXMuICANCkVuIG90cm8gcmVjaWVudGUgZXN0dWRpbywgc2UgZGVzY3JpYmnDsyBsYSBwcmV2YWxlbmNpYSB5IHViaWNhY2nDs24gZGUgbXV0YWNpb25lcyBzb23DoXRpY2FzIHkgcmUtb3JkZW5hbWllbnRvcyBjcm9tb3PDs21pY29zIGVuIG11ZXN0cmFzIGRlIHR1bW9yIGVuIHBhY2llbnRlcyBjb24gY8OhbmNlciBkZSBwcsOzc3RhdGEuIEFkZW3DoXMsIHNlIGlkZW50aWZpY2Fyb24gbXV0YWNpb25lcyBwcm9waWFzIGRlIHBhY2llbnRlcyBhZnJvYW1lcmljYW5vcywgcmVwb3J0YW5kbyB1bmEgbnVldmEgZnVzacOzbiBkZSBnZW5lcyBwcmVzZW50ZSBlbiBlbCAxNyUgZGUgbG9zIHBhY2llbnRlcyBkZWwgZXN0dWRpbyAoTGluZHF1aXN0IGV0IGFsLiAyMDE2KS4gIA0KRW4gbGEgaW52ZXN0aWdhY2nDs24gZGUgKEhlbyBldCBhbC4gMjAxNykgc2UgaWRlbnRpZmljYXJvbiBtdXRhY2lvbmVzIHNvbcOhdGljYXMgZW4gcGFjaWVudGVzIGNvcmVhbm9zIGNvbiBMZXVjZW1pYSBNaWVsb2lkZSBBZ3VkYSAoTE1BLCBBY3V0ZSBNeWVsb2lkIExldWtlbWlhKSwgY29uY2x1eWVuZG8gcXVlIGxvcyBzdWJ0aXBvcyBtb3Jmb2zDs2dpY29zIGRlIGxhIExNQSBwdWVkZW4gc2VyIHJlZmxlamFkb3MgcG9yIHBhdHJvbmVzIGVzcGVjw61maWNvcyBkZSBhbHRlcmFjaW9uZXMgZ2Vuw7NtaWNhcy4gQWRlbcOhcywgbG9zIGF1dG9yZXMgZXhwb25lbiBsYSBuZWNlc2lkYWQgZGUgZXN0dWRpb3MgZGUgZXN0ZSB0aXBvLCBsb3MgY3VhbGVzIHNvbiBtdXkgw7p0aWxlcyBwYXJhIGVsIGRpYWduw7NzdGljbyB0ZW1wcmFubyBkZSBlc3RhcyBlbmZlcm1lZGFkZXMgY29tcGxlamFzLiAgDQpFbiBlbCB0cmFiYWpvIGRlIChQZXJlaXJhIGV0IGFsLiAyMDE2KSwgbm8gc8OzbG8gaWRlbnRpZmljYW4gbG9zIDQwIGdlbmVzIGNvbiBtdXRhY2lvbmVzIGRyaXZlcnMgZW4gMi40MzMgbXVlc3RyYXMgZGUgdHVtb3IgZW4gY8OhbmNlciBwcmltYXJpbyBkZSBtYW1hLCBzaSBubyBxdWUgYnVzY2Fyb24gcGF0cm9uZXMgZGUgYXNvY2lhY2nDs24gZW50cmUgZXZlbnRvcyBtdXRhY2lvbmFsZXMgc29tw6F0aWNvcyBldmFsdWFuZG8gZWwgbml2ZWwgZGUgcmVsYWNpw7NuIGNvbiBsYSBzb2JyZXZpZGEgeSBwYXRyb25lcyBjbMOtbmljb3MuICANClNpIGJpZW4gbGEgaWRlbnRpZmljYWNpw7NuIGRlIGdlbmVzIGRyaXZlcnMgZXMgdW4gaW1wb3J0YW50ZSBhdmFuY2UgcGFyYSBsb2dyYXIgY29tcHJlbmRlciBsYSBiaW9sb2fDrWEgZGVsIGPDoW5jZXIsIGRldGVybWluYXIgcGF0cm9uZXMgZGUgZ2VuZXMgbXV0YWRvcyBxdWUgcGVybWl0YW4gZXN0cmF0aWZpY2FyIGxvcyBwYWNpZW50ZXMgYSBuaXZlbCBnZW7Ds21pY28gZXMgZWwgc2lndWllbnRlIHBhc28geSBudWV2byBkZXNhZsOtby4gIA0KUmVjaWVudGVzIGVzdHVkaW9zIGhhbiBwbGFudGVhZG8gcXVlIGxhIGNsYXNpZmljYWNpw7NuIGRlbCBjw6FuY2VyIGVuIHN1YnRpcG9zIGNsw61uaWNhIHkgYmlvbMOzZ2ljYW1lbnRlIHNpZ25pZmljYXRpdm9zLCBwdWVkZSBhdW1lbnRhciBsYSBlZmljaWVuY2lhIGRlIGxhIHByZWRpY2Npw7NuIGRlbCBkaWFnbsOzc3RpY28geSBzb2JyZXZpZGEsIGFzw60gY29tbyB0YW1iacOpbiwgbWVqb3JhciBsYSBlc3RyYXRlZ2lhIHRlcmFww6l1dGljYS4gU2luIGVtYmFyZ28sIGVzdGUgZW5mb3F1ZSBkZSBpbnZlc3RpZ2FjacOzbiBlcyBtdXkgcmVjaWVudGUsIHkgc8OzbG8gc2UgaGEgZXhwbG9yYWRvIGVsIGdlbm9tYSBkZSBsb3MgcHJpbmNpcGFsZXMgY8OhbmNlcmVzLCBkZWphbmRvIHVuYSBhbXBsaWEgbGlzdGEgZGUgb3Ryb3MgdGlwb3MgZGUgY8OhbmNlcmVzIHBhcmEgc3UgY2FyYWN0ZXJpemFjacOzbiB1dGlsaXphbmRvIHN1IGluZm9ybWFjacOzbiBnZW7Ds21pY2EuIFBvciB0YWwgbW90aXZvLCBlbCBwcmVzZW50ZSB0cmFiYWpvIHRpZW5lIHBvciBvYmpldGl2byBjcmVhciBwZXJmaWxlcyBnZW7Ds21pY29zIGRlIG11ZXN0cmFzIGRlIEFkZW5vY2FyY2lub21hIGRlIFB1bG3Ds24gZGUgKlRoZSBDYW5jZXIgR2Vub21lIEF0bGFzKiAoVENHQSkgcGFyYSBsYSBwcmVkaWNjacOzbiBkZSBzb2JyZXZpZGEgbWVkaWFudGUgbGEgaW50ZWdyYWNpw7NuIGRlIGFub3RhY2nDs24gY2zDrW5pY2EgeSBwYXRyb25lcyBtdXRhY2lvbmFsZXMuDQoNCg0KDQojIyM1LiBEZXNjcmlwY2nDs24gZGUgbG9zIGRhdG9zLg0KIExvcyBkYXRvcyBjb24gbG9zIHF1ZSB0cmFiYWphcmVtb3MgY29ycmVzcG9uZGVuIGEgMjMwIHJlZ2lzdHJvcyBkZSBwYWNpZW50ZXMgZGUgQWRlbm9jYXJjaW5vbWEgZGUgUHVsbcOzbiBleHRyYcOtZG9zIGRlICoqQ2Jpb3BvcnRhbCBmb3IgQ2FuY2VyIEdlbm9taWMqKi4gUG9yIGNhZGEgcGFjaWVudGUgc2UgdGllbmUgbGEgY2FudGlkYWQgZGUgbXV0YWNpb25lcyBlbiBjYWRhIHVubyBkZSBsb3MgZ2VuZXMgZGUgc3Ugc2VjdWVuY2lhIGRlIEFETjogQUJMMSwgQUtUMSwgQUtUMywgQVJBRiwgQVhMLCBCUkFGLCBDQ05EMSwgQ0RLNCwgQ0RLNiwgZXRjLiBBZGVtw6FzIHBvciBjYWRhIHBhY2llbnRlIHNlIHJlZ2lzdHJhbiB1bmEgc2VyaWUgZGUgZGF0b3MgY2zDrW5pY29zOiBnw6luZXJvLCBlZGFkLCB0aXBvIGRlIGPDoW5jZXIsIGVzdGFkw61vIGRlbCBjw6FuY2VyLCBlc3RhdHVzIGNvbW8gZnVtYWRvciwgY3VhbnRvcyBtZXNlcyBzb2JyZXZpdmnDsyBsdWVnbyBkZWwgZGlhZ27Ds3N0aWNvLCBlbnRyZSBvdHJvcy4NCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojQ2FyZ2FuZG8gZGF0b3MNCmxpYnJhcnkoZGF0YS50YWJsZSkNCg0Kc2V0d2QoIi4uL2RhdGEiKQ0KbXV0RnJlcSA8LSBmcmVhZCgibHVhZDIwMTRfbXV0X2ZyZXFfY2xpbmljYWxfZGF0YS50eHQiKQ0KY2FkZFNjbyA8LSBmcmVhZCgibHVhZDIwMTRfY2FkZF9zY29yZV9jbGluaWNhbF9kYXRhLnR4dCIpDQpgYGANCg0KQSBjb250aW51YWNpw7NuIHNlIG11ZXN0cmFuIGFsZ3VuYXMgZGUgbGFzIHByaW5jaXBhbGVzIGNhcmFjdGVyw61zdGljYXMgY2zDrW5pY2FzIGRlIGxvcyBkYXRvcyB1dGlsaXphZG9zLiANCg0KYGBge3IsIHdhcm5pbmc9RkFMU0V9DQoNCmxpYnJhcnkoZ2dwbG90MikNCnJlcXVpcmUoY293cGxvdCkNCg0KYmxhbmtfdGhlbWUgPC0gdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUoDQogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLA0KICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuZ3JpZD1lbGVtZW50X2JsYW5rKCksDQogICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCksDQogICAgcGxvdC50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMiwgZmFjZT0iYm9sZCIpDQogICkNCg0KI0dlbmRlcg0KZGF0YSA8LSBzZXROYW1lcyhubSA9IGMoIkdlbmRlciIsICJGcmVxIiksIGFzLmRhdGEuZnJhbWUodGFibGUobXV0RnJlcSRHRU5ERVIsIHVzZU5BPSJpZmFueSIpKSkNCmdlbmRlclBpZUNoYXJ0IDwtIGdncGxvdChkYXRhLCBhZXMoeD0iIiwgeT1GcmVxLCBmaWxsPUdlbmRlcikpICsNCiAgZ2VvbV9iYXIod2lkdGggPSAxLCBzdGF0ID0gImlkZW50aXR5IikgKw0KICBjb29yZF9wb2xhcigieSIsIHN0YXJ0PTApICsNCiAgYmxhbmtfdGhlbWUgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gRnJlcSksIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpLCBzaXplPTUpICsNCiAgZ2d0aXRsZSgiRGlzdHJpYnVjacOzbiBkZSBHw6luZXJvIikNCg0KDQojT3ZlcmFsbF9TdXJ2aXZhbF9TdGF0dXMNCnk8LXRhYmxlKG11dEZyZXEkT1NfU1RBVFVTLCB1c2VOQT0iaWZhbnkiKQ0KZGF0YSA8LSBzZXROYW1lcyhubSA9IGMoIk92ZXJhbGxfU3Vydml2YWxfU3RhdHVzIiwgIkZyZXEiKSwgYXMuZGF0YS5mcmFtZSh5KSkNCm92ZXJhbGxTdXJ2UGllQ2hhcnQgPC0gZ2dwbG90KGRhdGEsIGFlcyh4PSIiLCB5PUZyZXEsIGZpbGw9T3ZlcmFsbF9TdXJ2aXZhbF9TdGF0dXMpKSArDQogIGdlb21fYmFyKHdpZHRoID0gMSwgc3RhdCA9ICJpZGVudGl0eSIpICsNCiAgY29vcmRfcG9sYXIoInkiLCBzdGFydD0wKSArDQogIGJsYW5rX3RoZW1lICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IEZyZXEpLCBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSwgc2l6ZT01KSArIA0KICBnZ3RpdGxlKCJFc3RhZG8gZGUgU3VwZXJ2aXZlbmNpYSIpDQoNCg0KI0FHRQ0KYWdlSGlzdENoYXJ0IDwtIGdncGxvdChtdXRGcmVxLCBhZXMoeD1BR0UpKSArDQogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gNSwgbmEucm0gPSBUUlVFKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IG1lYW4obXV0RnJlcSRBR0UsIG5hLnJtID0gVFJVRSksIGNvbD0iYmx1ZSIpICsNCiAgZ2d0aXRsZSgiRGlzdHJpYnVjacOzbiBkZSBFZGFkIikNCg0KDQojVHVtb3Jfc3RhZ2VfMjAwOQ0KZGF0YSA8LSBzZXROYW1lcyhubSA9IGMoIlR1bW9yX3N0YWdlXzIwMDkiLCAiRnJlcSIpLCBhcy5kYXRhLmZyYW1lKHRhYmxlKG11dEZyZXEkVFVNT1JfU1RBR0VfMjAwOSwgdXNlTkE9ImlmYW55IikpKQ0KdHVtb3JTdGFnZVBpZUNoYXJ0IDwtIGdncGxvdChkYXRhLCBhZXMoeD0iIiwgeT1GcmVxLCBmaWxsPVR1bW9yX3N0YWdlXzIwMDkpKSArDQogIGdlb21fYmFyKHdpZHRoID0gMSwgc3RhdCA9ICJpZGVudGl0eSIpICsNCiAgY29vcmRfcG9sYXIoInkiLCBzdGFydD0wKSArDQogIGJsYW5rX3RoZW1lICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IEZyZXEpLCBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSwgc2l6ZT0zKSArIA0KICBnZ3RpdGxlKCJFc3RhZMOtbyBkZWwgdHVtb3IiKQ0KDQpwbG90X2dyaWQocGxvdF9ncmlkKGdlbmRlclBpZUNoYXJ0LCBvdmVyYWxsU3VydlBpZUNoYXJ0LCBhbGlnbj0naHYnKSwgcGxvdF9ncmlkKGFnZUhpc3RDaGFydCwgdHVtb3JTdGFnZVBpZUNoYXJ0KSwgbnJvdyA9IDIpDQoNCiNUT0JBQ0NPX1NNT0tJTkdfSElTVE9SWV9JTkRJQ0FUT1INCnk8LXRhYmxlKG11dEZyZXEkVE9CQUNDT19TTU9LSU5HX0hJU1RPUllfSU5ESUNBVE9SLCB1c2VOQT0iaWZhbnkiKQ0KZGF0YSA8LSBzZXROYW1lcyhubSA9IGMoIlRPQkFDQ09fU01PS0lOR19ISVNUT1JZX0lORElDQVRPUiIsICJGcmVxIiksIGFzLmRhdGEuZnJhbWUoeSkpDQoNCnNtb2tpbmdQaWVDaGFydCA8LSBnZ3Bsb3QoZGF0YSwgYWVzKHg9IiIsIHk9RnJlcSwgZmlsbCA9IFRPQkFDQ09fU01PS0lOR19ISVNUT1JZX0lORElDQVRPUikpICsNCiAgZ2VvbV9iYXIod2lkdGggPSAxLCBzdGF0ID0gImlkZW50aXR5IikgKw0KICBjb29yZF9wb2xhcigieSIsIHN0YXJ0PTApICsNCiAgYmxhbmtfdGhlbWUgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gRnJlcSksIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpLCBzaXplPTMpICsNCiAgZ2d0aXRsZSgiRXN0YXR1cyBkZSBmdW1hZG9yIikNCg0KDQojT3ZlcmFsbCBTdXJ2aXZhbCBWUyBUT0JBQ0NPX1NNT0tJTkdfSElTVE9SWV9JTkRJQ0FUT1INCm9zIDwtIGVjZGYobXV0RnJlcSRPU19NT05USFMpDQp4IDwtIG11dEZyZXFbLCBjKCJUT0JBQ0NPX1NNT0tJTkdfSElTVE9SWV9JTkRJQ0FUT1IiLCAiT1NfTU9OVEhTIildDQp4IDwtIG5hLm9taXQoeCkNCmxpYnJhcnkocGx5cikNCnggPC0gYXJyYW5nZSh4LCBUT0JBQ0NPX1NNT0tJTkdfSElTVE9SWV9JTkRJQ0FUT1IsIE9TX01PTlRIUykNCnguZWNkZiA8LSBkZHBseSh4LCAuKFRPQkFDQ09fU01PS0lOR19ISVNUT1JZX0lORElDQVRPUiksIHRyYW5zZm9ybSwgZWNkZiA9IGVjZGYoT1NfTU9OVEhTKShPU19NT05USFMpICkNCg0KU3Vydml2YWxDdW1CeVNtb2tpbmdTdGVwQ2hhcnQgPC0gZ2dwbG90KCkgKw0KICBnZW9tX3N0ZXAoZGF0YSA9IG11dEZyZXEsIGFlcyhPU19NT05USFMsIHkgPSAxMDAgKiAoMSAtIG9zKE9TX01PTlRIUykpLCBjb2xvdXIgPSAiQWxsIiksIHNpemUgPSAxLjIpICsNCiAgZ2VvbV9zdGVwKGRhdGEgPSB4LmVjZGYsIGFlcyhPU19NT05USFMsIHkgPSAxMDAgKiAoMSAtIGVjZGYpLCBjb2xvdXIgPSBUT0JBQ0NPX1NNT0tJTkdfSElTVE9SWV9JTkRJQ0FUT1IpKSArDQogIGxhYnModGl0bGUgPSAiU29icmV2aXZlbmNpYSIsIGNvbG91ciA9ICJUT0JBQ0NPX1NNT0tJTkdfSElTVE9SWSIsIHggPSAiTWVzZXMgU29icmV2aXZpZG9zIiwgeSA9ICJTb2JyZXZpdmVuY2lhICglKSIpDQoNCnBsb3RfZ3JpZChzbW9raW5nUGllQ2hhcnQsIFN1cnZpdmFsQ3VtQnlTbW9raW5nU3RlcENoYXJ0LCBhbGlnbj0naHYnLCBucm93ID0gMikNCg0KDQoNCiNPdmVyYWxsIFN1cnZpdmFsIFZTIEdlbmRlcg0KeCA8LSBtdXRGcmVxWywgYygiR0VOREVSIiwgIk9TX01PTlRIUyIpXQ0KeCA8LSBuYS5vbWl0KHgpDQpsaWJyYXJ5KHBseXIpDQp4IDwtIGFycmFuZ2UoeCwgR0VOREVSLCBPU19NT05USFMpDQp4LmVjZGYgPC0gZGRwbHkoeCwgLihHRU5ERVIpLCB0cmFuc2Zvcm0sIGVjZGYgPSBlY2RmKE9TX01PTlRIUykoT1NfTU9OVEhTKSApDQpTdXJ2aXZhbEN1bUJ5R2VuZGVyU3RlcENoYXJ0IDwtIGdncGxvdCgpICsNCiAgZ2VvbV9zdGVwKGRhdGEgPSB4LmVjZGYsIGFlcyhPU19NT05USFMsIHkgPSAxMDAgKiAoMSAtIGVjZGYpLCBjb2xvdXIgPSBHRU5ERVIpLCBzaXplID0gMC44KSArDQogIGxhYnModGl0bGUgPSAiU29icmV2aXZlbmNpYSIsIGNvbG91ciA9ICJHRU5ERVIiLCB4ID0gIk1lc2VzIFNvYnJldml2aWRvcyIsIHkgPSAiU29icmV2aXZlbmNpYSAoJSkiKQ0KDQpTdXJ2aXZhbEJ5R2VuZGVyQW5kVHVtb3JTdWJ0eXBlQmFyQ2hhcnQgPC0gZ2dwbG90KGFlcyh5ID0gT1NfTU9OVEhTLCB4ID0gSElTVE9MT0dJQ0FMX1NVQlRZUEUsIGZpbGwgPSBHRU5ERVIpLCBkYXRhID0gbmEub21pdChtdXRGcmVxWywgYygiT1NfTU9OVEhTIiwgIkhJU1RPTE9HSUNBTF9TVUJUWVBFIiwgIkdFTkRFUiIpXSkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIiwgbmEucm0gPSBUUlVFKSArDQogIGNvb3JkX2ZsaXAoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNTAsIGhqdXN0ID0gMSwgc2l6ZT04KSwgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZT04KSwgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpLCBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKw0KICB4bGFiKCJTdWJ0aXBvIGRlIFR1bW9yIikgKw0KICB5bGFiKCJNZXNlcyBTb2JyZXZpdmlkb3MiKQ0KDQpwbG90X2dyaWQoU3Vydml2YWxDdW1CeUdlbmRlclN0ZXBDaGFydCwgU3Vydml2YWxCeUdlbmRlckFuZFR1bW9yU3VidHlwZUJhckNoYXJ0LCBhbGlnbj0naHYnLCBucm93ID0gMikNCg0KDQoNClN1cnZpdmFsU21va2luZ0dlbmRlckJveHBsb3QgPC0gZ2dwbG90KGFlcyh5ID0gT1NfTU9OVEhTLCB4ID0gVE9CQUNDT19TTU9LSU5HX0hJU1RPUllfSU5ESUNBVE9SLCBmaWxsID0gR0VOREVSKSwgZGF0YSA9IG11dEZyZXEpICsNCiAgZ2VvbV9ib3hwbG90KCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDUwLCBoanVzdCA9IDEsIHNpemU9OCkpICsNCiAgeGxhYigiVGlwbyBkZSBmdW1hZG9yIikgKw0KICB5bGFiKCJPdmVyYWxsIHN1cnZpdmFsIikNCg0KI1N1cnZpdmFsU21va2luZ09TU3RhdHVzQm94cGxvdCA8LSBnZ3Bsb3QoYWVzKHkgPSBPU19NT05USFMsIHggPSBUT0JBQ0NPX1NNT0tJTkdfSElTVE9SWV9JTkRJQ0FUT1IsIGZpbGwgPSBPU19TVEFUVVMpLCBkYXRhID0gbXV0RnJlcSkgKw0KIyAgZ2VvbV9ib3hwbG90KCkgKw0KIyAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA1MCwgaGp1c3QgPSAxLCBzaXplPTgpKSArDQojICB4bGFiKCJUaXBvIGRlIGZ1bWFkb3IiKSArDQojICB5bGFiKCJPdmVyYWxsIHN1cnZpdmFsIikNCg0KI3Bsb3RfZ3JpZChTdXJ2aXZhbFNtb2tpbmdHZW5kZXJCb3hwbG90LCBTdXJ2aXZhbFNtb2tpbmdPU1N0YXR1c0JveHBsb3QsIGFsaWduPSd2JykNCg0KI1N1cnZpdmFsVHVtb3JTdGFnZUdlbmRlckJveHBsb3QgPC0gZ2dwbG90KGFlcyh5ID0gT1NfTU9OVEhTLCB4ID0gVFVNT1JfU1RBR0VfMjAwOSwgZmlsbCA9IEdFTkRFUiksIGRhdGEgPSBtdXRGcmVxKSArDQojICBnZW9tX2JveHBsb3QoKSArDQojICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDUwLCBoanVzdCA9IDEsIHNpemU9OCkpICsNCiMgIHhsYWIoIkVzdGFkaW8gZGUgdHVtb3IiKSArDQojICB5bGFiKCJPdmVyYWxsIHN1cnZpdmFsIikNCg0KI1N1cnZpdmFsVHVtb3JTdGFnZU9TU3RhdHVzQm94cGxvdCA8LSBnZ3Bsb3QoYWVzKHkgPSBPU19NT05USFMsIHggPSBUVU1PUl9TVEFHRV8yMDA5LCBmaWxsID0gT1NfU1RBVFVTKSwgZGF0YSA9IG11dEZyZXEpICsNCiMgIGdlb21fYm94cGxvdCgpICsNCiMgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNTAsIGhqdXN0ID0gMSwgc2l6ZT04KSkgKw0KIyAgeGxhYigiRXN0YWRpbyBkZSB0dW1vciIpICsNCiMgIHlsYWIoIk92ZXJhbGwgc3Vydml2YWwiKQ0KDQojcGxvdF9ncmlkKFN1cnZpdmFsVHVtb3JTdGFnZUdlbmRlckJveHBsb3QsIFN1cnZpdmFsVHVtb3JTdGFnZU9TU3RhdHVzQm94cGxvdCwgYWxpZ249J3YnKQ0KDQpTdXJ2aXZhbFR1bW9yU3VidHlwZUdlbmRlckJveHBsb3QgPC0gZ2dwbG90KGFlcyh5ID0gQUdFLCB4ID0gSElTVE9MT0dJQ0FMX1NVQlRZUEUsIGZpbGwgPSBHRU5ERVIpLCBkYXRhID0gbXV0RnJlcSkgKw0KICBnZW9tX2JveHBsb3QoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNTAsIGhqdXN0ID0gMSwgc2l6ZT04KSkgKw0KICB4bGFiKCJ0aXBvIGRlIHR1bW9yIikgKw0KICB5bGFiKCJPdmVyYWxsIHN1cnZpdmFsIikNCg0KI1N1cnZpdmFsVHVtb3JTdWJ0eXBlT1NTdGF0dXNCb3hwbG90IDwtIGdncGxvdChhZXMoeSA9IE9TX01PTlRIUywgeCA9IEhJU1RPTE9HSUNBTF9TVUJUWVBFLCBmaWxsID0gT1NfU1RBVFVTKSwgZGF0YSA9IG11dEZyZXEpICsNCiMgIGdlb21fYm94cGxvdCgpICsNCiMgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNTAsIGhqdXN0ID0gMSwgc2l6ZT04KSkgKw0KIyAgeGxhYigidGlwbyBkZSB0dW1vciIpICsNCiMgIHlsYWIoIk92ZXJhbGwgc3Vydml2YWwiKQ0KDQpwbG90X2dyaWQoU3Vydml2YWxTbW9raW5nR2VuZGVyQm94cGxvdCwgU3Vydml2YWxUdW1vclN1YnR5cGVHZW5kZXJCb3hwbG90LCBhbGlnbj0ndicpDQoNCg0KYGBgDQoNCg0KDQoNCmBgYHtyLCBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0KdGFibGEgPC1hZ2dyZWdhdGUoIE9TX01PTlRIUyB+IFRPQkFDQ09fU01PS0lOR19ISVNUT1JZX0lORElDQVRPUiwgbXV0RnJlcSwgRlVOID0gc3VtKQ0KdGFibGEyPC1kYXRhLmZyYW1lKFRPQkFDQ09fU01PS0lOR19ISVNUT1JZX0lORElDQVRPUj0gYygiQ3VycmVudCByZWZvcm1lZCBzbW9rZXIgZm9yID4gMTUgeWVhcnMiLCJMaWZlbG9uZyBOb24tc21va2VyIiwiQ3VycmVudCBzbW9rZXIiLCJDdXJyZW50IHJlZm9ybWVkIHNtb2tlciBmb3IgPCBvciA9IDE1IHllYXJzIiwiTkEiKSwgQ2FudGlkYWQ9Yyg2NywzMiw0NSw2OSwxNykpDQp0YWJsYTwtbWVyZ2UodGFibGEsdGFibGEyLGJ5ID0gIlRPQkFDQ09fU01PS0lOR19ISVNUT1JZX0lORElDQVRPUiIpDQp0YWJsYQ0KZ2dwbG90KHRhYmxhLCBhZXMoeD10YWJsYVssMV0sIHk9dGFibGFbLDJdL3RhYmxhWywzXSkpICsgDQogIGdlb21faGlzdG9ncmFtKHdpZHRoPTAuMiwgc3RhdD0iaWRlbnRpdHkiKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNTAsIGhqdXN0ID0gMSwgc2l6ZT04KSkNCmBgYA0KDQpFbiBsYSBzaWd1aWVudGUgdGFibGEgc2UgbXVlc3RyYSwgcG9yIGNhZGEgZ2VuIGxhIGNhbnRpZGFkIHRvdGFsIGRlIG11dGFjaW9uZXMgcHJlc2VudGVzIGVuIGxhIGRhdGEsIGNhbnRpZGFkIGRlIHBhY2llbnRlcyBxdWUgcHJlc2VudGFuIG11dGFjaW9uZXMgZW4gZXN0ZSBnZW4geSBlbCBwb3JjaWVudG8gcXVlIHJlcHJlc2VudGEgZGVsIHRvdGFsLg0KYGBge3J9DQojR2V0IHRvdGFsIG9mIG11dGF0aW9ucyBieSBnZW5lDQpnZW5lcyA8LSBzZXROYW1lcyhubSA9IGMoIkdlbmUiLCAiTXV0IiksIHN0YWNrKGNvbFN1bXMobXV0RnJlcVssIDIzOjE0NzgwXSkpWzI6MV0pDQoNCiNHZXQgdG90YWwgb2YgQ0FERCBTY29yZSBieSBnZW5lDQpwYXQgPC0gc2V0TmFtZXMobm0gPSBjKCJHZW5lIiwgIlNjb3JlIiksIHN0YWNrKGNvbFN1bXMoY2FkZFNjb1ssIDI6MTQ3NTldKSlbMjoxXSkNCg0KcGF0IDwtIG1lcmdlKHBhdCwgZ2VuZXNbLCBjKCJHZW5lIiwgIk11dCIpXSwgYnk9IkdlbmUiKQ0KDQojQWRkIHRvdGFsIG9mIHBhdGllbnRzIGJ5IG11dGF0ZWQgZ2VuZQ0KcGF0IDwtIG1lcmdlKHBhdCwgc2V0TmFtZXMobm0gPSBjKCJHZW5lIiwgIlBhdCIpLCBzdGFjayhjb2xTdW1zKGNhZGRTY29bLCAgMjoxNDc1OV0gLyBjYWRkU2NvWywgIDI6MTQ3NTldLCBuYS5ybSA9IFRSVUUpKVsyOjFdKSwgYnk9IkdlbmUiKQ0KDQpwYXQ8LSBwYXRbb3JkZXIocGF0JFNjb3JlLCBkZWNyZWFzaW5nID0gVFJVRSksIF0NCiNBZGQgQ0FERCBTY29yZSBvZiBtdXRhdGVkIGdlbmUgYnkgcGF0aWVudA0KcGF0JEZyZXEgPC0gYXBwbHkoZGF0YS50YWJsZShwYXRbLCA0XSksIDEsIGZ1bmN0aW9uKHgpe3JldHVybih4ICogMTAwIC8gMjMwKX0pDQpoZWFkKHBhdCkNCmBgYA0KDQoNCg0KDQojIyMjNS4xIFByZS1wcm9jZXNhbWllbnRvDQpMYSBtYXlvcsOtYSBkZSBlc3R1ZGlvcyBnZW7Ds21pY29zIHNlIGhhbiBiYXNhZG8gZW4gbWVkaWRhcyBvIGluZGljYWRvcmVzIHF1ZSBwZXJtaXRlbiBpbnRlcnByZXRhciB2YXJpYW50ZXMgZ2Vuw7NtaWNhcyB5IHJlbGFjaW9uYXJsYXMgY29uIGxhIGdlbmVyYWNpw7NuIHkgcHJvZ3Jlc2nDs24gZGVsIGPDoW5jZXIsIHBvciBlamVtcGxvLCBsYSBmcmVjdWVuY2lhIG11dGFjaW9uYWwuIEVuIHZhcmlvcyBlc3R1ZGlvcyBlc3RhIG1lZGlkYSBoYSBzaWRvIGNsYXZlIHBhcmEgbGEgaWRlbnRpZmljYWNpw7NuIGRlIGdlbmVzIGRyaXZlcnMuIFNpbiBlbWJhcmdvLCBvdHJhcyBpbnZlc3RpZ2FjaW9uZXMgcHJvcG9uZW4gdXRpbGl6YXIgZGlmZXJlbnRlcyBtZWRpZGFzIHF1ZSBjb3JyZWxhY2lvbmVuIGxhIGZ1bmNpb25hbGlkYWQgbW9sZWN1bGFyIGNvbW8gbGEgcGF0b2dlbmljaWRhZCBkZSBsYXMgbXV0YWNpb25lcyBlbiBsb3MgZ2VuZXMuICBFc3RhcyBtZWRpZGFzIHNvbiBjb25vY2lkYXMgY29tbyAqTWVhc3VyZXMgRGVsZXRlcmlvdXNuZXNzKi4gIA0KRW4gZWwgdHJhYmFqbyBkZSAoVnVyYWwsIFdhbmcsIGFuZCBHdWRhIDIwMTYpIHNlIHV0aWxpesOzIHVuIG3DqXRvZG8gcGFyYSBpbnRlZ3JhciBhbm90YWNpw7NuIGZ1bmNpb25hbCwgY29uc2VydmFjacOzbiBlIGluZm9ybWFjacOzbiBkZWwgbW9kZWxvIGRlbCBnZW4gZW4gdW4gw7puaWNvIHNjb3JlIGxsYW1hZG8gZWwgKkNvbWJpbmVkIEFubm90YXRpb24gRGVwZW5kZW50IERlcGxldGlvbiogKENBREQpIHNjb3JlIChLaXJjaGVyIGV0IGFsLiAyMDE0KSwgcXVlIGVzIHVuYSBNZXNhc3VyZXMgRGVsZXRlcmlvdXNuZXNzLiBFc3RlIHNjb3JlIHNlIGNvcnJlbGFjaW9uYSBjb24gbGEgZGl2ZXJzaWRhZCBhbMOpbGljYSwgYW5vdGFjaW9uZXMsIHBhdG9nZW5pY2lkYWQgZGUgbGFzIHZhcmlhbnRlcyBjb2RpZmljYW50ZXMsIGxvcyBlZmVjdG9zIHJlZ3VsYWRvcmVzIG1lZGlkb3MgZXhwZXJpbWVudGFsbWVudGUgeSB2YXJpYW50ZXMgY2F1c2FsZXMgZGUgZW5mZXJtZWRhZGVzIGRlbnRybyBkZWwgZ2Vub21hLiBFc3RlIHNjb3JlIHRvbWEgdmFsb3JlcyBlbiBlbCByYW5nbyArLSBpbmZpbml0bywgZG9uZGUgdW4gdmFsb3IgYWx0byBpbmRpY2EgZ3JhbmRlcyBlZmVjdG9zIGRlbGV0w6lyZW9zIGRlIGxhIG11dGFjacOzbi4gRW4gZXN0ZSB0cmFiYWpvIHNlIHByb3BvbmUgY3JlYXIgZG9zIHRpcG9zIGRlIHBlcmZpbGVzIGJhc2Fkb3MgZW4gbGEgZnJlY3VlbmNpYSBtdXRhY2lvbmFsIHkgZW4gdW5hIG1lZGlkYSBkZSBwYXRvZ2VuaWNpZGFkIGRlIG11dGFjaW9uZXMgIGNvbiBlbCBvYmpldGl2byBkZSBldmFsdWFybGFzIGNvbW8gbWVkaWRhcyBwcmVkaWN0b3JhcyBkZSBzb2JyZXZpZGEuDQoNCiMjIyMjNS4xLjEgUGVyZmlsIGJhc2FkbyBlbiBNdXRhdGlvbiBzY29yZQ0KRXN0ZSB0aXBvIGRlIHBlcmZpbCBjb3JyZXNwb25kZSBhIHVuYSBtYXRyaXogZGUgTXV0YXRpb24gU2NvcmUgX19fKG5fX18geCBfX19wKV9fXywgZG9uZGUgX19fbl9fXyBlcyBlbCBuw7ptZXJvIGRlIG11ZXN0cmFzIHkgX19fcF9fXyBlbCBuw7ptZXJvIGRlIGdlbmVzLiBDYWRhIGVudHJhZGEgX19fKGksIGopX19fIGNvbnRlbmRyw6EgbGEgc3VtYSBkZSBsb3MgQ0FERCBzY29yZSBkZSBsYXMgbXV0YWNpb25lcyBlbmNvbnRyYWRhcyBwYXJhIGxhIG11ZXN0cmEgX19faV9fXyBlbiBlbCBnZW4gX19fal9fXywgZXN0YSBzdW1hIGVzIGNvbm9jaWRhIGNvbW8gKk11dGF0aW9uIFNjb3JlKi4gTG9zIE11dGF0aW9uIFNjb3JlIHNlcsOhbiB0cmFuc2Zvcm1hZG9zIGEgdW5hIGVzY2FsYSBkZSAwIGEgaW5maW5pdG8gcG9zaXRpdm8uDQoNCiMjIyMjNS4yLjIgUGVyZmlsIGJhc2FkbyBlbiBGZWN1ZW5jaWEgbXV0YWNpb25hbA0KQSBlc3RlIHBlcmZpbCBjb3JyZXNwb25kZSBvdHJhIG1hdHJpeiBjb24gbGFzIG1pc21hcyBjYXJhY3RlcsOtc3RpY2FzIGVzdHJ1Y3R1cmFsZXMgX19fKG5fX18geCBfX19wKV9fXywgZG9uZGUgY2FkYSBjZWxkYSBfX18oaSwgailfX18gY29udGVuZHLDoSBsYSBmcmVjdWVuY2lhIHJlbGF0aXZhIGRlIG11dGFjaW9uZXMgcGFyYSBsYSBtdWVzdHJhIF9fX2lfX18gZW4gZWwgZ2VuIF9fX2pfX18uDQoNCg0KIyMjNi4gTWV0b2RvbG9nw61hDQojIyMjNi4xIFBlcmZpbGVzIG11dGFjaW9uYWxlcw0KRW4gY3VhbnRvIGEgbG9zIGRhdG9zIG11dGFjaW9uYWxlcywgc2UgY3JlYXLDoW4gcGVyZmlsZXMgbXV0YWNpb25hbGVzIGJhc2Fkb3MgZW4gZWwgTXV0YXRpb24gc2NvcmUsIGRlc2NyaXRvIHByZXZpYW1lbnRlLiBTZSBPYnRlbmRyw6EgdW5hIG1hdHJpeiBkb25kZSBsYXMgZmlsYXMgY29ycmVzcG9uZGVuIGEgbGFzIG11ZXN0cmFzIHkgbGFzIGNvbHVtbmFzIGEgbG9zIGdlbmVzIGNvbiBzdSBjb3JyZXNwb25kaWVudGUgc2NvcmUgZGUgbWFsaWduaWRhZC4gRXN0YSBtYXRyaXogc2Vyw6Egbm9ybWFsaXphZGEgZW50cmUgdmFsb3JlcyBkZSAwIHkgMSwgdXRpbGl6YW5kbyBlbCBtw6l0b2RvIGRlIG3DoXhpbW9zIHkgbcOtbmltb3MuDQoNCkVuIGxhIHNpZ3VpZW50ZSBmaWd1cmEgc2UgbXVlc3RyYSBlbCBmbHVqbyBkZSB0cmFiYWpvIHF1ZSBzZSByZWFsaXphcsOhIHBhcmEgbG9zIGRhdG9zIG11dGFjaW9uYWxlcy4NCg0KIVsqKkZsdWpvIGRlIHRyYWJham8gcGFyYSBkYXRvcyBkYXRvcyBtdXRhY2lvbmFsZXMqKl0oLi4vaW5mb3JtZXMvZmlndXJlcy93b3JrZmxvd19tdXRhY2lvbmVzLnBuZykNCg0KIyMjIzYuMiBEYXRvcyBjbMOtbmljb3MNCkVuIGN1YW5kbyBhIGxvcyBkYXRvcyBjbMOtbmljb3MsIGVzdG9zIHBhc2Fyw6FuIHBvciB1biBwcm9jZXNvIGRlIEJpbmFyaXphY2nDs24sIHVuIG3DqXRvZG8gcGFyYSBjb2RpZmljYXIgdmFyaWFibGVzIGNhdGVnw7NyaWNhcywgZG9uZGUgY2FkYSBjYXRlZ29yaWEgcGFyYSBjYWRhIGF0cmlidXRvIHNlcsOhIGNvbnZlcnRpZG8gZW4gdW4gbnVldm8gYXRyaWJ1dG8gY29uIHZhbG9yZXMgYmluYXJpb3MgKDAsMSkgcGFyYSBkZW5vdGFyIGxhIHByZXNlbmNpYSB5IGF1c2VuY2lhIGRlIGVzYSBjYXRlZ29yaWEgcGFyYSBjYWRhIG11ZXN0cmEuIFRhbWJpw6luLCBsb3MgZGF0b3MgbsO6bWVyaWNvcywgZW4gZXN0ZSBjYXNvIGxhIGVkYWQsIHNlcsOhIG5vcm1hbGl6YWRhIHNlZ8O6biBlbCBtw6l0b2RvIGRlIG3DoXhpbW9zIHkgbcOtbmltb3MuIFBvc3Rlcmlvcm1lbnRlLCBzZSBhbmFsaXphcsOhIGxhIGRpc3RyaWJ1Y2nDs24gZGUgbGEgc29icmV2aWRhLCB5YSBxdWUgcGFyYSBsb3MgbcOpdG9kb3MgZGUgcHJlZGljY2nDs24gcXVlIHNlIHV0bGl6YXLDoW4gc2UgcmVxdWllcmUgcXVlIGxhIHZhcmlhYmxlIHJlc3B1ZXN0YSwgZW4gZXN0ZSBjYXNvIHNvYnJldmlkYSwgcG9zZWEgdW5hIGRpc3RyaWJ1Y2nDs24gTm9ybWFsLiBQb3IgbG8gdGFudG8sIHNlIGV2YWx1YXLDoSBxdWUgZGlzdHJpYnVjacOzbiBwb3NlZSBlc3RhIHZhcmlhYmxlLCBzZSBjb3Jyb2JvcmFyw6EgbGEgbmVjZXNpZGFkIGRlIGVsaW1pbmFyIG91dGxpZXJzLCBzaSBleGlzdGVuLCBsb3MgY3VhbGVzIHB1ZWRlbiB0ZW5lciB1biBlZmVjdG8gbmVnYXRpdm8gZW4gZWwgYW7DoWxpc2lzLCB5IHRhbWJpw6luIHNlIHZlcsOhIHNpIGVzIG5lY2VzYXJpbyBhcGxpY2FyIHVuYSB0cmFuc2Zvcm1hY2nDs24gKGxvZ2FyaXRtbykgcGFyYSBxdWUgbGEgc29icmV2aWRhIHRlbmdhIHVuYSBkaXN0cmlidWNpw7NuIGRlbCB0aXBvIE5vcm1hbC4NCkVuIGxhIHNpZ3VpZW50ZSBmaWd1cmEgc2UgbXVlc3RyYSBlbCBmbHVqbyBkZSB0cmFiYWpvIHF1ZSBzZSByZWFsaXphcsOhIHBhcmEgbG9zIGRhdG9zIGNsw61uaWNvcw0KDQohWyoqRmx1am8gZGUgdHJhYmFqbyBwYXJhIGRhdG9zIGRhdG9zIG11dGFjaW9uYWxlcyoqXSguLi9pbmZvcm1lcy9maWd1cmVzL3dvcmtmbG93X2NsaW5pY2FfZGF0YS5wbmcpDQoNCg0KbHVlZ28sIGxhcyBtYXRyaWNlcyByZXN1bHRhbnRlcyBkZSBsb3MgZGF0b3MgbXV0YWNpb25hbGVzIHkgY2zDrW5pY29zIHNlcsOhbiB1bmlkb3MgcG9yIGVsIElkIGRlIGxhcyBtdWVzdHJhcywgY3JlYW5kb3NlIHVuYSBtYXRyaXogbyBwZXJmaWwgY2zDrW5pY28tbXV0YWNpb25hbC4gUG9zdGVyaW9ybWVudGUsIGVzdGEgbWF0cml6IHNlcsOhIGRpdmlkaWRhIGVuIGRvcyBzZXQgZGUgZGF0b3MsIGVsIGRlIGVudHJlbmFtaWVudG8sIGNvbiBlbCA3NSUgZGUgbG9zIGRhdG9zIHkgZWwgZGUgdGVzdCBjb24gZWwgMjUlIHJlc3RhbnRlIGRlIGZvcm1hIGFsZWF0b3JpYS4gTG9zIGFsZ29yaXRtb3MgcXVlIHNlIGFwbGljYXLDoW4gc2Vyw6FuIGVudHJlbmFkb3MgY29uIGVsIHNldCBkZSBlbnRyZW5hbWllbnRvIHkgbHVlZ28gc2UgYXBsaWNhcsOhbiBjb24gZWwgc2V0IHRlc3QuIEVuIGVzdGUgdHJhYmFqbyBzZSBhcGxpY2Fyw6FuIGFsZ29yaXRtb3MgZGUgcHJlZGljY2nDs24geSBkZSBjbGFzaWZpY2FjacOzbi4NCg0KIVsqKkZsdWpvIGRlIHRyYWJham8gcGFyYSBsYSBwcmVkaWNjacOzbiBkZSBsYSBzb2JyZXZpZGEqKl0oLi4vaW5mb3JtZXMvZmlndXJlcy9tZXJnZV9jbGluaWNhbF9tdXRhdGlvbi5wbmcpDQoNCg0KIyMjIzYuMyAgQWxnb3JpdG1vcyBkZSBwcmVkaWNjacOzbg0KDQojIyMjIzYuMy4xIFByaW5jaXBhbCBDb21wb25lbnQgUmVncmVzc2lvbiAoUENSKQ0KRWwgbcOpdG9kbyBQcmluY2lwYWwgQ29tcG9uZW50IFJlZ3Jlc3Npb24gKFBDUiksICBhIGRpZmVyZW5jaWEgZGUgbGFzIHTDqWNuaWNhcyBkZSByZWdyZXNpw7NuIHRyYWRpY2lvbmFsZXMgcXVlIHRyYWJhamFuIGRpcmVjdGFtZW50ZSBzb2JyZSBsb3MgZGF0b3Mgb3JpZ2luYWxlcywgIGVzIGNhcGF6IGRlIGV4dHJhZXIgbG9zIGNvbXBvbmVudGVzIHByaW5jaXBhbGVzIChQQ3MpLCBxdWUgc29uIGNvbWJpbmFjaW9uZXMgbGluZWFsZXMgZGUgbG9zIGZlYXR1cmVzIG9yaWdpbmFsZXMsIHJlYWxpemFuZG8gbHVlZ28gdW5hIHJlZ3Jlc2nDs24gbGluZWFsIGNvbiBsb3MgY29lZmljaWVudGVzIGRlIGxvcyBQQ3Mgb2J0ZW5pZG9zLiBQb3IgbG8gZ2VuZXJhbCwgIHPDs2xvIHVuIHN1YmNvbmp1bnRvIGRlIFBDcyBlcyBlbGVnaWRvIHBhcmEgbGEgcmVncmVzacOzbi4gTGEgc2VsZWNjacOzbiB0cmFkaWNpb25hbCBzZSBiYXNhIGVuIGlkZW50aWZpY2FyIGxvcyBQQ3MgY29uIHVuYSBhbHRhIHZhcmlhbnphIHkgcXVlIGVzdMOpbiBhbHRhbWVudGUgYXNvY2lhZG9zIGEgbGEgdmFyaWFibGUgcmVzcHVlc3RhLlNlIHV0aWxpemFyw6EgbGEgZnVuY2nDs24gKnByY29tcCogcGFyYSBjYWxjdWxhciBsb3MgY29tcG9uZW50ZXMgcHJpbmNpcGFsZXMgKFBDcyksIHkgbGEgZnVuY2nDs24gKmxtKiBwYXJhIGFqdXN0YXIgZWwgbW9kZWxvIGxpbmVhbC4NCg0KIyMjIyM2LjMuMiBQYXJ0aWFsIExlYXN0IFNxdWFyZXMgUmVncmVzc2lvbiAoUExTUikgDQpQYXJ0aWFsIExlYXN0IFNxdWFyZXMgUmVncmVzc2lvbiAoUExTUikgZXMgdW5hIGFsdGVybmF0aXZhIHN1cGVydmlzYWRhIGEgUENSLiBBbCBpZ3VhbCBxdWUgbGEgUENSLCBQTFNSIGVzIHVuIG3DqXRvZG8gZGUgcmVkdWNjacOzbiBkZSBkaW1lbnNpb25lcywgcXVlIHByaW1lcm8gaWRlbnRpZmljYSB1biBudWV2byBjb25qdW50byBtw6FzIHBlcXVlw7FvIGRlIGNhcmFjdGVyw61zdGljYXMgcXVlIHNvbiBjb21iaW5hY2lvbmVzIGxpbmVhbGVzIGRlIGxhcyBjYXJhY3RlcsOtc3RpY2FzIG9yaWdpbmFsZXMsIGx1ZWdvIHNlIGFqdXN0YSB1biBtb2RlbG8gbGluZWFsIGEgdHJhdsOpcyBkZSBtw61uaW1vcyBjdWFkcmFkb3MgYSBsYXMgbnVldmFzIGNhcmFjdGVyw61zdGljYXMgKk0qLiBTaW4gZW1iYXJnbywgYSBkaWZlcmVuY2lhIGRlIFBDUiwgUExTUiBoYWNlIHVzbyBkZSBsYSB2YXJpYWJsZSBkZSByZXNwdWVzdGEgcGFyYSBpZGVudGlmaWNhciBsYXMgbnVldmFzIGNhcmFjdGVyw61zdGljYXMuIFNlIHVzYXLDoSBsYSBmdW5jacOzbiAqcGxzciogZGVsIHBhcXVldGUgcGxzIHBhcmEgcmVhbGl6YXIgZXN0ZSBhbsOhbGlzaXMuIA0KDQojIyMjIzYuMy4zIFJlZ3Jlc3Npb24gVHJlZQ0KTG9zIMOhcmJvbGVzIGRlIHByZWRpY2Npw7NuIHNlIHV0aWxpemFuIHBhcmEgcHJlZGVjaXIgdW5hIHJlc3B1ZXN0YSBvIGNsYXNlICRZJC4gcGFyYSBsYXMgZW50cmFkYXMgJFhfMSwgWF8yLCBYXzMsIC4uLiwgWF9uJC4gU2kgZXMgdW5hIHJlc3B1ZXN0YSBjb250aW51YSwgc2UgbGxhbWEgdW4gw6FyYm9sIGRlIHJlZ3Jlc2nDs24sIHNpIGVzIGNhdGVnw7NyaWNvLCBzZSBsbGFtYSDDoXJib2wgZGUgY2xhc2lmaWNhY2nDs24uIEVuIGNhZGEgbm9kbyBkZWwgw6FyYm9sLCBzZSBjb21wcnVlYmEgZWwgdmFsb3IgZGUgdW5hIGVudHJhZGEgJFhfaSQNCnkgZGVwZW5kaWVuZG8gZGUgbGEgcmVzcHVlc3RhIChiaW5hcmlhKSBzZSBjb250aW7DumEgaGFjaWEgbGEgc3ViLXJhbWEgaXpxdWllcmRhIG8gaGFjaWEgbGEgZGVyZWNoYS4gQ3VhbmRvIHNlIGxsZWdhIGEgdW5hIGhvamEsIHNlIGVuY3VlbnRyYSBsYSBwcmVkaWNjacOzbiAoZ2VuZXJhbG1lbnRlIGVzIHVuYSBlc3RhZMOtc3RpY2Egc2ltcGxlIGRlbCBjb25qdW50byBkZSBkYXRvcyBxdWUgcmVwcmVzZW50YSBsYSBob2phLCBjb21vIGVsIHZhbG9yIG3DoXMgY29tw7puIGRlIGxhcyBjbGFzZXMgZGlzcG9uaWJsZXMpLg0KRW4gZXN0ZSBjYXNvLCBzZSB1dGxpemFyw6EgdW4gw6FyYm9sIGRlIHByZWRpY2Npw7NuLCB5YSBxdWUgbGEgc29icmV2aWRhIGVzIG7Dum1lcmljYS4gUGFyYSBlc3RvIHNlIHV0aWxpemFyw6EgbGEgZnVuY2nDs24gKnJwYXJ0KiBkZWwgcGFxdWV0ZSBycGFydC4NCg0KDQoNCiMjIzcuIFJlc3VsdGFkb3MNCkVuIGN1YW50byBzb2JyZXZpZGEsIGVzdGEgcHJlc2VudMOzIHVuYSBkaXN0cmlidWNpw7NuIGV4cG9uZW5jaWFsLCBwb3IgbG8gdGFudG8gc2UgYXBsaWPDsyB1bmEgdHJhbnNmb3JtYWNpw7NuIGxvZ2Fyw610bWljYS4gVGFtYmnDqW4sIHNlIGVuY29udHLDsyBkb3Mgb3V0bGllcnMgbG9zIGN1YWxlcyBmdWVyb24gcmVtb3ZpZG9zIGRlbCBzZXQgZGUgZGF0b3MuDQoNCiFbKipEaXN0cmlidWNpw7NuIG9yaWdpbmFsIGRlIGxhIHNvYnJldmlkYSoqXSguLi9pbmZvcm1lcy9maWd1cmVzL2Rpc3RyaV9zb2JyZXZpZGFfb3JpZ2luYWwucG5nKQ0KDQoNCg0KIVsqKkRpc3RyaWJ1Y2nDs24gdHJhbnNmb3JtYWRhIHkgZmlsdHJhZGEgZGUgbGEgc29icmV2aWRhKipdKC4uL2luZm9ybWVzL2ZpZ3VyZXMvZGlzdHJpX3NvYnJldmlkYV9sb2cucG5nKQ0KDQoNCg0KIyMjIzcuMiBQcmluY2lwYWwgQ29tcG9uZW50IGFuYWx5c2lzIChQQ0EpIGNvbiByZWdyZXNpw7NuIGxpbmVhbCBkZSBsb3MgUENzIHNpZ25pZmljYXRpdm9zDQoNCkxvcyBkYXRvcyBmdWVyb24gY2VudHJhbGl6YWRvcyB5IGVzY2FsYWRvcywgY29tbyByZXF1ZXJpbWllbnRvIGRlIFBDQS4gTHVlZ28sIHNlIGNhbGN1bMOzIFBDQSB1c2FuZG8gbGEgZnVuY2nDs24gcHJjb21wKCkuIExvcyBwcmltZXJvcyAxMCBjb21wb25lbnRlcyBmdWVyb24gbG9zIHF1ZSBleHBsaWNhbiB1bmEgbWF5b3IgcHJvcG9yY2nDs24gZGUgbGEgdmFyaWFuemEgZGUgbG9zIGRhdG9zLiBMdWVnbywgZXN0b3MgMTAgUENzIHNlIGluZ3Jlc2Fyb24gY29tbyBwcmVkaWN0b3JlcyBlbiB1biBtb2RlbG8gZGUgcmVncmVzacOzbiBsaW5lYWwgZG9uZGUgbGEgdmFyaWFibGUgcmVzcHVlc3RhIGVzIGxhIHNvYnJldmlkYS4gRWwgcmVzdWx0YWRvIGRlIGVzdG8sIGVudHJlZ8OzIDUgUENzIHNpZ25pZmljYXRpdm9zLCBQQzEsIFBDMiwgUEM2LA0KIFBDNyB5IFBDMTAsIGxvcyBjdWFsZXMgZnVlcm9uIHVzYWRvcyBjb21vIHByZWRpY3RvcmVzIGVuIHVuIG51ZXZvIG1vZGVsbyBkZSByZWdyZXNpw7NuIGxpbmVhbC4gRXN0ZSDDumx0aW1vIG1vZGVsbyBmdWUgdXNhZG8gcGFyYSBsYSBwcmVkaWNjacOzbiBjb24gZWwgc2V0IHNldC4NCg0KIVsqKkZsdWpvIGRlIHRyYWJham8gcGFyYSBQQ0EgY29uIHJlZ3Jlc2nDs24gbGluZWFsKipdKC4uL2luZm9ybWVzL2ZpZ3VyZXMvd29ya2Zsb3dfcGNhLnBuZykNCg0KDQpQYXJhIGxvcyByZXN1bHRhZG9zIGRlIGxhIHByZWRpY2Npw7NuIHNlIGNhbGN1bG8gZWwgTWVhbiBTcXVhcmVzIEVycm9yLCBlbCBjdWFsIGZ1ZSBkZSAwLjgwNDczMzguDQoNCiMjIyM3LjMgUGFydGlhbCBMZWFzdCBTcXVhcmVzIFJlZ3Jlc3Npb24gKFBMU1IpIA0KUGFyYSBkZXRlcm1pbmFyIGVsIG7Dum1lcm8gZGUgY29tcG9uZW50ZXMgcXVlIHNlIHV0aWxpesOzIGVuIGxhIHJlZ3Jlc2nDs24sIHNlIGVzdGltYSBlbCBNZWFuIHNxdWFyZWQgZXJyb3Igb2YgcHJlZGljdGlvbiAoTVNFUCkgdXNhbmRvIGVsIG3DqXRvZG8gZGUgY3Jvc3ZhbGlkYWNpw7NuLiBBIGNvbnRpbnVhY2nDs24sIGVuIGxhIHNpZ3VpZW50ZSBmaWd1cmEgc2UgbXVlc3RyYSBlbCBNU0VQIHBhcmEgbG9zIGNvbXBvbmVudGVzIHByaW5jaXBhbGVzLCBkb25kZSBlbiBlbCAxMDBQQyBoYXkgdW4gcHVudG8gZGUgaW5mbGVjY2nDs24uDQoNCg0KIVsqKk1TRVAgcGFyYSBsb3MgUENzKipdKC4uL2luZm9ybWVzL2ZpZ3VyZXMvTVNFUC5wbmcpDQoNCg0KUG9zdGVyaW9ybWVudGUsIHNlIHJlYWxpesOzIGxhIHByZWRpY2Npw7NuIHVzYW5kbyBlbCBuw7ptZXJvIGRlIFBDcyBjb24gbWVqb3IgTVNFUCB5IGVsIHNldCB0ZXN0LiANCkVsIE1TRSBkZSBsYSBwcmVkaWNjacOzbiBjb24gZWwgc2V0IGRlIGRhdG9zIGZ1ZSBkZSA2LjE4MzQ3Mi4NCg0KDQojIyMjNy40IFJlZ3Jlc3Npb24gVHJlZQ0KDQpFbiBsYSBzaWd1aWVudGUgZmlndXJhIHNlIG11ZXN0cmEgZWwgbW9kZWxvIGRlIHJlZ3Jlc3Npb24gdHJlZSBjcmVhZG8gY29uIGVsIHNldCBkZSBlbnRyZW5hbWllbnRvLiBTZSBwdWVkZSBvYnNlcnZhciBxdWUgbGEgbWF5b3LDrWEgZGUgbG9zIG5vZG9zIGNvcnJlc3BvbmRlbiBhIHZhcmlhYmxlcyBjbMOtbmljYXMsIHkgcXVlIGxvcyBnZW5lcyBxdWUgYWxsw60gc2UgZW5jdWVudHJhbiBubyBzb24gZW4gc3UgbWF5b3LDrWEgZ2VuZXMgZnJlY3VlbnRlbWVudGUgbXV0YWRvcy4gRWwgdmFsb3IgZGUgTVNFIGRlIGxhIHByZWRpY2Npw7NuIGNvbiBlbCBzZXQgdGVzdCBmdWUgZGUgMi40NDc4NzQuIA0KIVsqKk1TRVAgcGFyYSBsb3MgUENzKipdKC4uL2luZm9ybWVzL2ZpZ3VyZXMvVFJFRS5wbmcpDQoNCg0KIyMjIzcuNSBBbGdvcml0bW9zIGRlIENsYXNpZmljYWNpw7NuDQpMYSBTb2JyZXZpZGEgZXMgdW5hIHZhcmlhYmxlIGNvbnRpbnVhLCBwZXJvIHBhcmEgdHJhdGFyIGVsIHByb2JsZW1hIGNvbW8gdW4gcHJvYmxlbWEgZGUgY2xhc2lmaWNhY2nDs24gc2UgaGl6byBuZWNlc2FyaW8gdHJhbnNmb3JtYXJsYSBhIHVuYSB2YXJpYWJsZSBjYXRlZ8OzcmljYS4gRXN0byBzZSBoaXpvIGNyZWFuZG8gNiBjbGFzZXMgcXVlIGN1YnJpZXJhbiBkaXN0aW50b3MgcmFuZ29zIGRlIHRpZW1wbyBlbiBtZXNlcyBxdWUgcmVwcmVzZW50YW4gbGEgZGlzdHJpYnVjacOzbiBkZSBsYSBTb2JyZXZpZGEuIFNlIGludGVudMOzIGVuIHByaW1lciBsdWdhciBxdWUgbGFzIGNsYXNlcyBlc3R1dmllcmFuIGJhbGFuY2VhZGFzLCB5IGx1ZWdvIHF1ZSByZWZsZWphcmFuIHJhbmdvcyBkZSB0aWVtcG9zIHJlcHJlc2VudGF0aXZvcyBwYXJhIGVsIHByb2JsZW1hLiBBIGNvbnRpbnVhY2nDs24gc2UgbXVlc3RyYSB1bmEgZGVzY3JpcGNpw7NuIGRlIGVzdGEgbnVldmEgdmFyaWFibGUgY2F0ZWfDs3JpY2EgKE9TX0NMQVNTKS4NCg0KYGBge3J9DQpkYXRhIDwtIGNhZGRTY28NCmRhdGEkT1NfQ0xBU1MgPC0gYXMuZmFjdG9yKGN1dChkYXRhJE9TX01PTlRIUywgYygwLCAxLCA2LCAxMiwgMjQsIDM2LCAzMDApLCBpbmNsdWRlLmxvd2VzdCA9IFRSVUUpKQ0Kc3VtbWFyeShkYXRhJE9TX0NMQVNTKQ0KcXBsb3QoeCA9IE9TX0NMQVNTLCBkYXRhID0gZGF0YSkNCmBgYA0KDQpQYXJhIHRyYXRhciBlbCBwcm9ibGVtYSBzZSB1c2Fyb24gbG9zIHNpZ3VpZW50ZXMgYWxnb3JpdG1vcyBkZSBjbGFzaWZpY2FjacOzbjogIA0KDQoqIER1bW15IENsYXNzaWZpZXIgKEJhc2UpICANCiogTG9naXN0aWMgUmVncmVzc2lvbiAoTFIpICANCiogTGluZWFyIERpc2NyaW1pbmFudCBBbmFseXNpcyAoTERBKSAgDQoqIEtOTmVpZ2hib3JzIENsYXNzaWZpZXIgKEtOTikgIA0KKiBEZWNpc2lvbiBUcmVlIENsYXNzaWZpZXIgKENBUlQpICANCiogR2F1c3NpYW4gTmFpdmUgQmF5ZXMgKE5CKSAgDQoqIFN1cHBvcnQgVmVjdG9yIE1hY2hpbmUgKFNWTSkgIA0KDQpMYSBleHBlcmltZW50YWNpw7NuIHNlIHJlYWxpesOzIHVzYW5kbyBzb2xhbWVudGUgbG9zIGdlbmVzICJkcml2ZXJzIiwgZXMgZGVjaXIgbG9zIDUzIGdlbmVzIHF1ZSBzZSBjb25zaWRlcmFuIG3DoXMgZnVlcnRlbWVudGUgYXNvY2lhZG9zIGFsIGRlc2Fycm9sbG8gZGVsIGPDoW5jZXIuIFBhcmEgY29tcHJvYmFyIGxvcyByZXN1bHRhZG9zIG9idGVuaWRvcywgc2UgcmVhbGl6w7MgYWRlbcOhcyB1biB0ZXN0IGVzdGFkw61zdGljbyBwYXJhIHZlciBzaSBsb3MgY2xhc2lmaWNhZG9yZXMgZXJhbiBzaWduaWZpY2F0aXZhbWVudGUgZGlmZXJlbnRlcywgb2J0ZW5pw6luZG9zZSAgDQoNCmBgYHtweXRob24gZWNobz1UUlVFLCBldmFsPVRSVUUsIGluY2x1ZGU9VFJVRX0NCiMgTG9hZCBsaWJyYXJpZXMNCmltcG9ydCBwYW5kYXMNCmZyb20gcGFuZGFzLnRvb2xzLnBsb3R0aW5nIGltcG9ydCBzY2F0dGVyX21hdHJpeA0KDQppbXBvcnQgbWF0cGxvdGxpYi5weXBsb3QgYXMgcGx0DQoNCmZyb20gc2tsZWFybiBpbXBvcnQgbW9kZWxfc2VsZWN0aW9uDQpmcm9tIHNrbGVhcm4ubWV0cmljcyBpbXBvcnQgY2xhc3NpZmljYXRpb25fcmVwb3J0DQpmcm9tIHNrbGVhcm4ubWV0cmljcyBpbXBvcnQgY29uZnVzaW9uX21hdHJpeA0KZnJvbSBza2xlYXJuLm1ldHJpY3MgaW1wb3J0IGFjY3VyYWN5X3Njb3JlDQoNCmZyb20gc2tsZWFybi5saW5lYXJfbW9kZWwgaW1wb3J0IExvZ2lzdGljUmVncmVzc2lvbg0KZnJvbSBza2xlYXJuLnRyZWUgaW1wb3J0IERlY2lzaW9uVHJlZUNsYXNzaWZpZXINCmZyb20gc2tsZWFybi5uZWlnaGJvcnMgaW1wb3J0IEtOZWlnaGJvcnNDbGFzc2lmaWVyDQpmcm9tIHNrbGVhcm4uZGlzY3JpbWluYW50X2FuYWx5c2lzIGltcG9ydCBMaW5lYXJEaXNjcmltaW5hbnRBbmFseXNpcw0KZnJvbSBza2xlYXJuLm5haXZlX2JheWVzIGltcG9ydCBHYXVzc2lhbk5CDQpmcm9tIHNrbGVhcm4uc3ZtIGltcG9ydCBTVkMNCmZyb20gc2tsZWFybi5kdW1teSBpbXBvcnQgRHVtbXlDbGFzc2lmaWVyDQoNCmZyb20gc3RhdHNtb2RlbHMuc3RhdHMud2VpZ2h0c3RhdHMgaW1wb3J0IHR0ZXN0X2luZA0KDQoNCmltcG9ydCBudW1weSBhcyBucA0KZnJvbSBza2xlYXJuLm1vZGVsX3NlbGVjdGlvbiBpbXBvcnQgdHJhaW5fdGVzdF9zcGxpdA0KDQojIyBydW5fY2xhc3NpZmllciByZWNpYmUgdW4gY2xhc2lmaWNhZG9yLCB1biBkYXRhc2V0IChYLCB5KSANCiMjIHkgb3BjaW9uYWxtZW50ZSBsYSBjYW50aWRhZCBkZSByZXN1bHRhZG9zIHF1ZSBzZSBxdWllcmUgb2J0ZW5lciBkZWwgY2xhc2lmaWNhZG9yDQpkZWYgcnVuX2NsYXNzaWZpZXIoY2xmLCBYLCB5LCBudW1fdGVzdHM9MTApOg0KICAgIHNjb3JlcyA9IFtdDQogICAgDQogICAgZm9yIF8gaW4gcmFuZ2UobnVtX3Rlc3RzKToNCiAgICAgICAgIyMjIENPTVBMRVRBUiBBQ8OBDQogICAgICAgIFhfdHJhaW4sIFhfdGVzdCwgeV90cmFpbiwgeV90ZXN0ID0gdHJhaW5fdGVzdF9zcGxpdChYLCB5LCB0ZXN0X3NpemU9MC4yNSkNCiAgICAgICAgY2xmLmZpdChYX3RyYWluLCB5X3RyYWluKQ0KICAgICAgICBzY29yZXMuYXBwZW5kKCBjbGYuc2NvcmUoWF90ZXN0LCB5X3Rlc3QpICkgICMgWF90ZXN0IHkgeV90ZXN0IGRlYmVuIHNlciBkZWZpbmlkb3MgcHJldmlhbWVudGUNCiAgICANCiAgICByZXR1cm4gbnAuYXJyYXkoc2NvcmVzKQ0KICAgIA0KDQoNCiMjIHJ1bl9jbGFzc2lmaWVycyByZWNpYmUgdW4gZGF0YXNldCAoWCwgeSkNCmRlZiBydW5fY2xhc3NpZmllcnMoWCwgeSk6DQogICAgIyBTcG90IENoZWNrIEFsZ29yaXRobXMNCiAgICBtb2RlbHMgPSBbXQ0KICAgIG1vZGVscy5hcHBlbmQoKCdCYXNlJywgRHVtbXlDbGFzc2lmaWVyKHN0cmF0ZWd5PSdzdHJhdGlmaWVkJykpKQ0KICAgIG1vZGVscy5hcHBlbmQoKCdMUicsIExvZ2lzdGljUmVncmVzc2lvbigpKSkNCiAgICAjbW9kZWxzLmFwcGVuZCgoJ0xEQScsIExpbmVhckRpc2NyaW1pbmFudEFuYWx5c2lzKCkpKQ0KICAgIG1vZGVscy5hcHBlbmQoKCdLTk4nLCBLTmVpZ2hib3JzQ2xhc3NpZmllcigpKSkNCiAgICBtb2RlbHMuYXBwZW5kKCgnQ0FSVCcsIERlY2lzaW9uVHJlZUNsYXNzaWZpZXIoKSkpDQogICAgbW9kZWxzLmFwcGVuZCgoJ05CJywgR2F1c3NpYW5OQigpKSkNCiAgICBtb2RlbHMuYXBwZW5kKCgnU1ZNJywgU1ZDKCkpKQ0KDQogICAgcmVzdWx0X2xpc3QgPSBbXQ0KICAgIHRhYmxlX2hlYWQgPSAiTkFNRVMiICsgIlx0IiArICJBY2N1cmFjeSINCiAgICBmb3IgbmFtZSwgY2xmIGluIG1vZGVsczoNCiAgICAgICAgdGFibGVfaGVhZCArPSAiXHQiICsgbmFtZQ0KICAgICAgICBhY2N1cmFjeXMgPSBydW5fY2xhc3NpZmllcihjbGYsIFgsIHksIDQwKQ0KICAgICAgICByZXN1bHRfbGlzdC5hcHBlbmQoKG5hbWUsIGFjY3VyYWN5cykpDQoNCiAgICBwcmludCgiKyBpbmRpY2EgZGlmZXJlbmNpYSBzaWduaWZpY2F0aXZhXG4iKQ0KICAgIHByaW50KHRhYmxlX2hlYWQpDQogICAgZm9yIG5hbWUxLCByZXN1bHRzMSBpbiByZXN1bHRfbGlzdDoNCiAgICAgICAgdGFibGVfcm93ID0gbmFtZTEgKyAiXHQiICsgc3RyKHJlc3VsdHMxLm1lYW4oKSkNCiAgICAgICAgI3ByaW50KCJDb21wYXJhbmRvICVzIC0gQWNjdXJhY3k6ICUuMmYiICUgKG5hbWUxLCByZXN1bHRzMS5tZWFuKCkpKQ0KICAgICAgICBmb3IgbmFtZTIsIHJlc3VsdHMyIGluIHJlc3VsdF9saXN0Og0KICAgICAgICAgICAgdGFibGVfcm93ID0gdGFibGVfcm93ICsgIlx0Ig0KICAgICAgICAgICAgaWYgbmFtZTEgPT0gbmFtZTI6DQogICAgICAgICAgICAgICAgY29udGludWUNCg0KICAgICAgICAgICAgXywgcF92YWx1ZSwgX18gPSB0dGVzdF9pbmQocmVzdWx0czEsIHJlc3VsdHMyKSAgIyB0LXRlc3Q6IHRlc3QgZXN0YWRpc3RpY28NCiAgICAgICAgDQogICAgICAgICAgICBpZiBwX3ZhbHVlIDw9IDAuMDU6DQogICAgICAgICAgICAgICAgc2lnID0gIisiDQogICAgICAgICAgICBlbHNlOg0KICAgICAgICAgICAgICAgIHNpZyA9ICIiDQogICAgICAgICAgICB0YWJsZV9yb3cgPSB0YWJsZV9yb3cgKyBzaWcNCiAgICAgICAgICAgICNwcmludCgiJXM6XHQlLjJmICVzIiAlIChuYW1lMiwgcmVzdWx0czIubWVhbigpLCBzaWcpKQ0KICAgICAgICBwcmludCh0YWJsZV9yb3cpDQoNCg0KDQoNCiMgTG9hZCBjbGluaWNhbCBkYXRhc2V0DQpkYXRhc2V0ID0gcGFuZGFzLnJlYWRfY3N2KCIuLi9kYXRhL2NsaW5pY2FsX2RhdGFfYmluX3dpdGhfT1NfQ2xhc3NfQUdFX05PUk0uY3N2IikNCg0KDQojQ2xpbmljYWwgRGF0YQ0KZGF0YSA9IGRhdGFzZXQuZHJvcChbJ09TX01PTlRIUyddLCBheGlzID0gMSkuZHJvcChbJ0FHRV9OT1JNJ10sIGF4aXMgPSAxKQ0KWCA9IGRhdGEudmFsdWVzWzosIDE6KGRhdGEuY29sdW1ucy5zaXplLTEpIF0NCnkgPSBkYXRhLnZhbHVlc1s6LCBkYXRhLmNvbHVtbnMuc2l6ZS0xXQ0KDQpwcmludCgiUmVzdWx0YWRvcyBkZSBDbGFzaWZpY2Fkb3JlcyBjb24gRGF0b3MgQ2zDrW5pY29zXG4iKQ0KcnVuX2NsYXNzaWZpZXJzKFgsIHkpDQpwcmludCgpDQpwcmludCgpDQoNCg0KDQojIExvYWQgQ0FERCA1MyBkcml2ZXIgZ2VuZXMgZGF0YQ0KZGF0YXNldF9nZW5fbnVtXzUzID0gcGFuZGFzLnJlYWRfY3N2KCIuLi9kYXRhL2dlbmVfbnVtXzUzLmNzdiIpDQojZGF0YXNldF9nZW5fY2FkZCA9IHBhbmRhcy5yZWFkX2NzdigiLi4vZGF0YS9nZW5lX2NhZGRfbm9ybS5jc3YiKQ0KDQoNCiMgQ0FERCA1MyBEcml2ZXIgR2VuZXMgRGF0YQ0KI3ByaW50KGRhdGFzZXQubG9jWzosIFsnUm93Lm5hbWVzJywgJ09TX0NMQVNTJ11dKQ0KZGF0YSA9IHBhbmRhcy5tZXJnZShkYXRhc2V0X2dlbl9udW1fNTMsIGRhdGFzZXQubG9jWzosIFsnUm93Lm5hbWVzJywgJ09TX0NMQVNTJ11dLCBvbiA9ICdSb3cubmFtZXMnKQ0KWCA9IGRhdGEudmFsdWVzWzosIDE6KGRhdGEuY29sdW1ucy5zaXplLTEpXQ0KeSA9IGRhdGEudmFsdWVzWzosIGRhdGEuY29sdW1ucy5zaXplLTFdDQoNCnByaW50KCJSZXN1bHRhZG9zIGRlIENsYXNpZmljYWRvcmVzIGNvbiBEYXRvcyBHZW7DqXRpY29zIChDQUREKVxuIikNCnJ1bl9jbGFzc2lmaWVycyhYLCB5KQ0KcHJpbnQoKQ0KcHJpbnQoKQ0KDQoNCmRhdGFzZXRfZ2VuX251bV81MyA9IHBhbmRhcy5yZWFkX2NzdigiLi4vZGF0YS9nZW5lX251bV81My5jc3YiKQ0KI2RhdGFzZXRfZ2VuX2NhZGQgPSBwYW5kYXMucmVhZF9jc3YoIi4uL2RhdGEvZ2VuZV9jYWRkX25vcm0uY3N2IikNCg0KZGF0YSA9IHBhbmRhcy5tZXJnZShkYXRhc2V0X2dlbl9udW1fNTMsIGRhdGFzZXQsIG9uID0gJ1Jvdy5uYW1lcycpLmRyb3AoWydPU19NT05USFMnXSwgYXhpcyA9IDEpLmRyb3AoWydBR0VfTk9STSddLCBheGlzID0gMSkNClggPSBkYXRhLnZhbHVlc1s6LCAxOihkYXRhLmNvbHVtbnMuc2l6ZS0xKV0NCnkgPSBkYXRhLnZhbHVlc1s6LCBkYXRhLmNvbHVtbnMuc2l6ZS0xXQ0KDQpwcmludCgiUmVzdWx0YWRvcyBkZSBDbGFzaWZpY2Fkb3JlcyBjb24gRGF0b3MgQ2zDrW5pY29zIHkgR2Vuw6l0aWNvc1xuIikNCnJ1bl9jbGFzc2lmaWVycyhYLCB5KQ0KDQpgYGANCg0KTG9zIHJlc3VsdGFkb3Mgb2J0ZW5pZG9zIG5vIHNvbiBtdXkgYnVlbm9zLCBkZXN0YWPDoW5kb3NlIGxvcyBjbGFzaWZpY2Fkb3JlcyBMb2dpc3RpYyBSZWdyZXNzaW9uIHkgRGVjaXNpb24gVHJlZSBjb21vIGxvcyBkZSBtZWpvcmVzIHJlc3VsdGFkb3MuIFNlIGhhY2UgbmVjZXNhcmlvIHVuIGFuw6FsaXNpcyBtw6FzIGRldGFsbGFkbyBkZSBsYXMgY2F1c2FzIGRlIGVzdG9zIHJlc3VsdGFkb3MuDQoNCg0KDQojIyM4LiBDb25jbHVzaW9uZXMgeSB0cmFiYWpvIGZ1dHVybw0KKiBFbiBsb3MgbcOpdG9kb3MgZGUgcHJlZGljY2nDs24sIGxvcyBwZXJmaWxlcyBjbMOtbmljby1tdXRhY2lvbmFsIHRpZW5lbiB1biBtZW5vciBNU0UuICANCiogRW4gbG9zIG3DqXRvZG9zIGRlIGNsYXNpZmljYWNpw7NuLCBsb3MgcGVyZmlsZXMgY2zDrW5pY28geSBjbMOtbmljby1tdXRhY2lvbmFsIG1vc3RyYXJvbiB1biBtZWpvciBhY2N1cmFjeSwgYXVucXVlIG5vIHNlIG9idHV2aWVyb24gbXV5IGJ1ZW5vcyByZXN1bHRhZG9zIGVuIGdlbmVyYWwuICANCiogVXNhciBvdHJvcyBzZXQgZGUgZGF0b3MgY29uIG1heW9yIGNhbnRpZGFkIGRlIG11ZXN0cmFzLiAgDQoqIERldGVjdGFyIHkgZXhwbG9yYXIgbGEgZXN0cnVjdHVyYSBpbnRlcm5hIGRlIGxvcyBkYXRvcyBjb24gdMOpY25pY2FzIGRlIE1pbmVyw61hIGRlIERhdG9zIChvdHJvcyBhbGdvcml0bW9zIGRlIGFncnVwYW1pZW50bywgZXRjLikuICANCiogRXhwZXJpbWVudGFjacOzbiBjb24gb3RyYXMgdMOpY25pY2FzIGRlIHJlZHVjY2nDs24gZGUgZGltZW5zaW9uYWxpZGFkLCB5IG90cm9zIGFsZ29yaXRtb3MgZGUgcmVncmVzacOzbiB5IGRlIGNsYXNpZmljYWNpw7NuLiAgDQoqIEFwbGljYXIgb3RyYXMgbcOpdHJpY2FzIHBhcmEgY29tcGFyYXIgbG9zIHJlc3VsdGFkb3MgZGUgbGFzIHByZWRpY2Npb25lcy4gIA0KKiBJZGVudGlmaWNhciBsb3MgY29tcG9uZW50ZXMgcXVlIGNvbnRyaWJ1eWVuIGVuIGxhIHNvYnJldmlkYS4gIA0KDQoNCiMjIzkuIEJpYmxpb2dyYWbDrWENCg0KKipDaGVuZywgRmVpeGlvbmcsIEp1bmZlaSBaaGFvLCBhbmQgWmhvbmdtaW5nIFpoYW8qKi4gMjAxNi4gIkFkdmFuY2VzIGluIENvbXB1dGF0aW9uYWwgQXBwcm9hY2hlcyBmb3IgUHJpb3JpdGl6aW5nIERyaXZlciBNdXRhdGlvbnMgYW5kIFNpZ25pZmljYW50bHkgTXV0YXRlZCBHZW5lcyBpbiBDYW5jZXIgR2Vub21lcy4iIEJyaWVmaW5ncyBpbiBCaW9pbmZvcm1hdGljcyAxNyAoNCk6IDY0Mi01Ni4gZG9pOjEwLjEwOTMvYmliL2JidjA2OC4gIA0KKipEaW1pdHJha29wb3Vsb3MsIENocmlzdG9zIE0uLCBhbmQgTmlrbyBCZWVyZW53aW5rZWwqKi4gMjAxNy4gIkNvbXB1dGF0aW9uYWwgQXBwcm9hY2hlcyBmb3IgdGhlIElkZW50aWZpY2F0aW9uIG9mIENhbmNlciBHZW5lcyBhbmQgUGF0aHdheXMuIiBXaWxleSBJbnRlcmRpc2NpcGxpbmFyeSBSZXZpZXdzLiBTeXN0ZW1zIEJpb2xvZ3kgYW5kIE1lZGljaW5lIDkgKDEpLiBkb2k6MTAuMTAwMi93c2JtLjEzNjQuICANCioqR2FycmF3YXksIExldmkgQS4sIGFuZCBFcmljIFMuIExhbmRlcioqLiAyMDEzLiAiTGVzc29ucyBmcm9tIHRoZSBDYW5jZXIgR2Vub21lLiIgQ2VsbCAxNTMgKDEpOiAxNy0zNy4gZG9pOjEwLjEwMTYvai5jZWxsLjIwMTMuMDMuMDAyLiAgDQoqKkhlbywgU2VvbmcgR3UsIFlvdW5naWwgS29oLCBKb25nIEt3YW5nIEtpbSwgSm9uZ3N1biBKdW5nLCBIeXVuZy1MYWUgS2ltLCBTdW5nLVNvbyBZb29uLCBhbmQgSmkgV2FuIFBhcmsqKi4gMjAxNy4gIklkZW50aWZpY2F0aW9uIG9mIFNvbWF0aWMgTXV0YXRpb25zIFVzaW5nIFdob2xlLUV4b21lIFNlcXVlbmNpbmcgaW4gS29yZWFuIFBhdGllbnRzIHdpdGggQWN1dGUgTXllbG9pZCBMZXVrZW1pYS4iIEJNQyBNZWRpY2FsIEdlbmV0aWNzIDE4IChNYXJjaCkuIGRvaToxMC4xMTg2L3MxMjg4MS0wMTctMDM4Mi15LiAgDQoqKktpcmNoZXIsIE1hcnRpbiwgRGFuaWVsYSBNIFdpdHRlbiwgUHJldGkgSmFpbiwgQnJpYW4gSiBPJ1JvYWssIEdyZWdvcnkgTSBDb29wZXIsIGFuZCBKYXkgU2hlbmR1cmUqKi4gMjAxNC4gIkEgR2VuZXJhbCBGcmFtZXdvcmsgZm9yIEVzdGltYXRpbmcgdGhlIFJlbGF0aXZlIFBhdGhvZ2VuaWNpdHkgb2YgSHVtYW4gR2VuZXRpYyBWYXJpYW50cy4iIE5hdHVyZSBHZW5ldGljcyA0NiAoMyk6IDMxMC0xNS4gZG9pOjEwLjEwMzgvbmcuMjg5Mi4gIA0KKipLb3UsIFRhZGF5dWtpLCBNYXNhc2hpIEthbmFpLCBTaGlnZW1pIE1hdHN1bW90bywgWWFzdXNoaSBPa3VubywgYW5kIE1hbmFidSBNdXRvKiouIDIwMTYuICJUaGUgUG9zc2liaWxpdHkgb2YgQ2xpbmljYWwgU2VxdWVuY2luZyBpbiB0aGUgTWFuYWdlbWVudCBvZiBDYW5jZXIuIiBKYXBhbmVzZSBKb3VybmFsIG9mIENsaW5pY2FsIE9uY29sb2d5IDQ2ICg1KTogMzk5LTQwNi4gZG9pOjEwLjEwOTMvampjby9oeXcwMTguICANCioqTGksIFNoaXlvbmcsIFlvb24tTGEgQ2hvaSwgWmh1b2xpbiBHb25nLCBYaWFvIExpdSwgTWFydWphIExpcmEsIFpoZW5neWFuIEthbiwgRW5zZWwgT2gsIGV0IGFsKiouIDIwMTYuICJDb21wcmVoZW5zaXZlIENoYXJhY3Rlcml6YXRpb24gb2YgT25jb2dlbmljIERyaXZlcnMgaW4gQXNpYW4gTHVuZyBBZGVub2NhcmNpbm9tYS4iIEpvdXJuYWwgb2YgVGhvcmFjaWMgT25jb2xvZ3kgMTEgKDEyKTogMjEyOS00MC4gZG9pOjEwLjEwMTYvai5qdGhvLjIwMTYuMDguMTQyLiAgDQoqKkxpbmRxdWlzdCwgS2FybGEgSi4sIFBhbWVsYSBMLiBQYXJpcywgVGhvbWFzIEouIEhvZmZtYW5uLCBOaWFsbCBKLiBDYXJkaW4sIFLDqW1pIEthem1hLCBKb2VsIEEuIE1lZmZvcmQsIEplZmZyZXkgUC4gU2lta28sIGV0IGFsKiouIDIwMTYuICJNdXRhdGlvbmFsIExhbmRzY2FwZSBvZiBBZ2dyZXNzaXZlIFByb3N0YXRlIFR1bW9ycyBpbiBBZnJpY2FuIEFtZXJpY2FuIE1lbi4iIENhbmNlciBSZXNlYXJjaCA3NiAoNyk6IDE4NjAtNjguIGRvaToxMC4xMTU4LzAwMDgtNTQ3Mi5DQU4tMTUtMTc4Ny4gIA0KKipOaWstWmFpbmFsLCBTZXJlbmEqKi4gMjAxNC4gIkluc2lnaHRzIGludG8gQ2FuY2VyIEJpb2xvZ3kgdGhyb3VnaCBuZXh0LUdlbmVyYXRpb24gU2VxdWVuY2luZy4iIENsaW5pY2FsIE1lZGljaW5lIDE0IChTdXBwbCA2KTogczcxLTc3LiBkb2k6MTAuNzg2MS9jbGlubWVkaWNpbmUuMTQtNi1zNzEuICANCioqUGVyZWlyYSwgQmVybmFyZCwgU3VldC1GZXVuZyBDaGluLCBPc2NhciBNLiBSdWVkYSwgSGFucy1LcmlzdGlhbiBNb2VuIFZvbGxhbiwgRWxlbmEgUHJvdmVuemFubywgSGVsZW4gQS4gQmFyZHdlbGwsIE1pY2hlbGxlIFB1Z2gsIGV0IGFsKiouIDIwMTYuICJUaGUgU29tYXRpYyBNdXRhdGlvbiBQcm9maWxlcyBvZiAyLDQzMyBCcmVhc3QgQ2FuY2VycyBSZWZpbmUgVGhlaXIgR2Vub21pYyBhbmQgVHJhbnNjcmlwdG9taWMgTGFuZHNjYXBlcy4iIE5hdHVyZSBDb21tdW5pY2F0aW9ucyA3IChNYXkpOiAxMTQ3OS4gZG9pOjEwLjEwMzgvbmNvbW1zMTE0NzkuICANCioqUmlhemFsaG9zc2VpbmksIFlhc3NlciwgYW5kIE1hcmsgTGF0aHJvcCoqLiAyMDE2LiAiUHJlY2lzaW9uIE1lZGljaW5lIGZyb20gdGhlIFJlbmFsIENhbmNlciBHZW5vbWUuIiBOYXR1cmUgUmV2aWV3cyBOZXBocm9sb2d5IDEyLiBkb2k6MTAuMTAzOC9ucm5lcGguMjAxNi4xMzMuICANCioqVnVyYWwsIFN1bGV5bWFuLCBYaWFvc2hlbmcgV2FuZywgYW5kIENoaXR0aWJhYnUgR3VkYSoqLiAyMDE2LiAiQ2xhc3NpZmljYXRpb24gb2YgQnJlYXN0IENhbmNlciBQYXRpZW50cyBVc2luZyBTb21hdGljIE11dGF0aW9uIFByb2ZpbGVzIGFuZCBNYWNoaW5lIExlYXJuaW5nIEFwcHJvYWNoZXMuIiBCTUMgU3lzdGVtcyBCaW9sb2d5IDEwIChTdXBwbCAzKS4gZG9pOjEwLjExODYvczEyOTE4LTAxNi0wMzA2LXouICANCioqWWFuZywgV2lsbGlhbSwgS2VuamkgWW9zaGlnb2UsIFhpYW5nIFFpbiwgSnVuIFMgTGl1LCBKYWNrIFkgWWFuZywgQW5kcnplaiBOaWVtaWVya28sIFlvdXBpbmcgRGVuZywgZXQgYWwqKi4gMjAxNC4gIklkZW50aWZpY2F0aW9uIG9mIEdlbmVzIGFuZCBQYXRod2F5cyBJbnZvbHZlZCBpbiBLaWRuZXkgUmVuYWwgQ2xlYXIgQ2VsbCBDYXJjaW5vbWEuIiBCTUMgQmlvaW5mb3JtYXRpY3MgMTUgKFN1cHBsIDE3KTogUzIuIGRvaToxMC4xMTg2LzE0NzEtMjEwNS0xNS1TMTctUzIuICANCioqWmhhbmcsIEZhbiwgQ2h1bnlhbiBSZW4sIEt3dW4gS2l0IExhdSwgWmloYW4gWmhlbmcsIEdlbWluZyBMdSwgWmhlbmd6aSBZaSwgWW9uZ3pob25nIFpoYW8sIGV0IGFsKiouIDIwMTYuICJBIE5ldHdvcmsgTWVkaWNpbmUgQXBwcm9hY2ggdG8gQnVpbGQgYSBDb21wcmVoZW5zaXZlIEF0bGFzIGZvciB0aGUgUHJvZ25vc2lzIG9mIEh1bWFuIENhbmNlci4iIEJyaWVmaW5ncyBpbiBCaW9pbmZvcm1hdGljcyAxNyAoNik6IDEwNDQtNTkuIGRvaToxMC4xMDkzL2JpYi9iYncwNzYuDQoNCg0KPC9kaXY+