El procedimiento de Kaplan-Meier.

De acuerdo a [2], con el método de Kaplan-Meier, también llamadao el método del producto límite, podemos usar los datos que se recoletan para analizar y estimar la probabilidad de sobrevivir por un tiempo determinado. Este método es una técnica no parametrica, es decir, no se hacen suposiciones sobre la distribución de los tiempos se sobrevivencia.

Para este método, consideramos lo siguiente:

A partir de estas estimaciónes de las proporciones \(\hat{p}_k\) podemos calcular la probabilidad estimada de sobrevivir en un tiempo \(t\), la cual está dada por:

\[\hat{S}(t) = \prod_{i = 1}^{t}\hat{p}_i\]

Es importante mencionar que podemos entender las proporciones \(\hat{p}_k\) como probabilidades condicionales: la probabilidad de sobrevivir otro periodo de tiempo dado que ya sobreviví los \((k-1)\) anteriores.

Ejemplo.

Este ejemplo es presentado y desarrollado en [2]. En este ejemplo 14.3.1, haciendo referencia al estudio de Martini et al [1], se analiza el tiempo de supervivencia de personas que tuvieron tumores. En específico, se tienen dos tipos de tumores (y dos datasets en consecuencia), los de grado bajo (con 25 personas en esta categoria) y los de grado alto (con 14 personas). Nuestro objetivo es comparar el tiempo de supervivencia entre ambos grupos, para lo que usaremos el método de Kaplan-Meier.

Primeramente, importamos y mostramos la estructura de los datos:

df <- read.csv('EXA_C14_S03_01.csv')
head(df)

Podemos observar que tenemos información del tiempo de supervivencia, y el tipo de tomor (L ⮕ grado bajo, H⮕g rado alto). Además, tenemos tres tipos de evento:

Ahora, es necesario operar estos datos para obtener las proporciones \(\hat{p}_k\), así como la función de supervivencia \(\hat{S}(t)\).

# Separamos por tipo de tumor
df_l <- df[ which(df$TUMOR=='L'), ]
df_h <- df[ which(df$TUMOR=='H'), ]

# Función para conocer estatus:
get.status <- function(x){
  if(x == 'ned'){
    return(0)
  }
  else{
    return(1)
  }
}

# Definimos función que realiza kaplan-meier
kaplan.meier <- function(df){
  time <- c() # Arreglo para guardar parte temporal
  risk <- c() # Pacientes en riesgo (aun vivos)
  alive <- c() # Pacientes vivos al final del periodo
  proportion <- c() # Proporcions (vivos/riesgo)
  cumulative <- c() # Función cumulativa de proporciones S(t)
  
  # Intervalo de tiempo de interés
  max <- max(df$TIME)
  min <- min(df$TIME)
  
  # Número total de pacientes
  n <- length(df$SUBJ)

  # Obtenemos proporciones en orden
  for (k in min:max){
    
    # Obtenemos pacientes con ese tiempo
    aux = df[which(df$TIME==k),]
    
    # En caso de tener paciente
    if(length(aux$VITAL) > 0){
      # Lista para estatus de pacientes
      stat <- c()
      for(j in 1:length(aux$VITAL)){
        stat <- append(stat, get.status(aux$VITAL[j]))
      }
      
      # Si sucedió algo, calculamos todo
      if(is.element(1, stat)){
        time <- append(time, k)
        die <- sum(stat == 1)
        survive <- sum(stat == 0)
        
        risk <- append(risk, n)
        n <- n - die
        alive <- append(alive, n)
        prop <- n/(n+die)
        proportion<- append(proportion, prop)
        if(length(cumulative) == 0){
          cumulative <- proportion[1]
        }
        else{
          cumulative <- append(cumulative,prop*cumulative[length(cumulative)])
        }
        n <- n - survive
      }
      else{
        # Quitamos pacientes que no mueren
        n <- n-length(aux$VITAL)
      }
    }
  
    }
  df_ans <- data.frame(time, risk, alive, proportion, cumulative)
  return(df_ans)
}
  

Con esta función, obtenemos la información relevante para ambos tumores. Primero, para el grado bajo:

ans_l <- kaplan.meier(df_l)
ans_l

Y ahora, el grado alto:

ans_h <- kaplan.meier(df_h)
ans_h

Finalmente, graficamos:

plot(x = append(ans_l$time, 212), y = append(1, ans_l$cumulative),
     type = 'S',
     col = 'blue',
     main = 'Curva de supervivencia de Kaplan-Meier', 
     xlab = 'Tiempo (meses)',
     ylab = 'Probabilidad',
     ylim = c(0, 1), 
     xlim = c(0, 120))
lines(append(ans_h$time, 140), append(1, ans_h$cumulative), type = 'S', col = 'red')

# Etiquetas de líneas.
legend("topright", c("Grado bajo", "Grado alto"), fill=c("blue", "red"))
abline(v = 60)
abline(h = 0.5, col = 'gray')

# Agregamos una cuadrícula.
grid()

Algunas métricas importantes:

  1. Mediana del tiempo de supervivencia: Corresponde con el punto donde la función cumulativa de supervivencia toda \(0.5\).

    En el caso del grupo de grado bajo, esto es 212 meses, y en el grupo de grado alto es de tan solo 9 meses.

  2. Proporción de supervivencia a 5 años: Observamos a la función a los 12 años (60 meses).

    Para el grupo de grado bajo, esto es de \(0.73\) y el grado alto es de \(0.07\).

  3. Tiempo promedio se supervivencia: Corresponde a dividir el tiempo total de vida entre todos los sujetos (sumado), dividido entre el número de sujetos.

    Para el grupo de grado bajo:

total <- sum(df_l$TIME)
print(total/length(df_l$VITAL))
[1] 88.04
Y para el grupo de grado alto: 
total <- sum(df_h$TIME)
print(total/length(df_h$VITAL))
[1] 18.35714

Referencias.

  1. Nael Martini, Andrew Michael E. Burt, Robert T. heelan, Manjit S. Bains, Patricia M. McCormack, Vlerie W. Rsch, Michael Weber, Robert J. Downey y Robert J. Ginsberg, Predictions of Survival in Malignant Tumors of the Sternum, Journal of Thoracic and Cardiovasvular Surgery, 111 (1996), 95-106
  2. Wayne W. Daniel y Chad L. Cross, Biostatistics: A Foundation for Analysis in the Health Sciences, 10° edition, Wiley.

Anexos.

Diapositivas de presentación de proyecto.

Se pueden encontrar en este link.

LS0tDQp0aXRsZTogIlByb3llY3RvIDIgLSBUZXJjZXIgcGFyY2lhbCAtIEVzdGFkw61zdGljYSBhdmFuemFkYS4iDQphdXRob3I6ICJHYWJyaWVsIE1pc3NhZWwgQmFyY28uIE5VQTogNDI3MDcxIHwgTGF1cmEgZGVsIENhcm1lbiBDYWJhbCBQYXJhbW8uIE5VQTogMzkxNTgwIg0KZGF0ZTogIjI5IGRlIE5vdmllbWJyZSBkZSAyMDIxIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KPHN0eWxlPg0KYm9keSB7dGV4dC1hbGlnbjoganVzdGlmeTsgZm9udC1zaXplOiAxMnB0fQ0KPC9zdHlsZT4NCg0KPGNlbnRlcj4NCiAgICA8aW1nIHdpZHRoPSI2MCUiIHNyYz0iaHR0cHM6Ly9pLmltZ3VyLmNvbS9VYW85Q0l5LnBuZyI+DQo8L2NlbnRlcj4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyBFbCBwcm9jZWRpbWllbnRvIGRlIEthcGxhbi1NZWllci4NCg0KRGUgYWN1ZXJkbyBhIFsyXSwgY29uIGVsIG3DqXRvZG8gZGUgS2FwbGFuLU1laWVyLCB0YW1iacOpbiBsbGFtYWRhbyBlbCBtw6l0b2RvIGRlbCBwcm9kdWN0byBsw61taXRlLCBwb2RlbW9zIHVzYXIgbG9zIGRhdG9zIHF1ZSBzZSByZWNvbGV0YW4gcGFyYSBhbmFsaXphciB5IGVzdGltYXIgbGEgcHJvYmFiaWxpZGFkIGRlIHNvYnJldml2aXIgcG9yIHVuIHRpZW1wbyBkZXRlcm1pbmFkby4gRXN0ZSBtw6l0b2RvIGVzIHVuYSB0w6ljbmljYSBubyBwYXJhbWV0cmljYSwgZXMgZGVjaXIsIG5vIHNlIGhhY2VuIHN1cG9zaWNpb25lcyBzb2JyZSBsYSBkaXN0cmlidWNpw7NuIGRlIGxvcyB0aWVtcG9zIHNlIHNvYnJldml2ZW5jaWEuDQoNClBhcmEgZXN0ZSBtw6l0b2RvLCBjb25zaWRlcmFtb3MgbG8gc2lndWllbnRlOiANCg0KLSAkbj0kIG7Dum1lcm8gdG90YWwgZGUgc3VqZXRvcyBkZSBsb3MgcXVlIGNvbm9jZW1vcyBlbCB0aWVtcG8gZGUgc29icmV2aXZlbmNpYS4NCg0KLSAkXGhhdHtwfV8xPSQgRXN0aW1hY2nDs24gZGUgbGEgcHJvcG9yY2nDs24gZGUgc3VqZXRvcyBxdWUgc29icmV2aXZlbiBlbCBwcmltZXIgcGVyaW9kbyBkZSB0aWVtcG8uDQoNCi0gJFxoYXR7cH1faz0kIEVzdGltYWNpw7NuIGRlIGxhIHByb3BvcmNpw7NuIHNlIHN1amV0b3MgcXVlIHNvYnJldml2ZW4gZWwgcGVyaW9kbyBkZSB0aWVtcG8gJGskIGRlc3B1w6lzIGRlIGhhYmVyIHNvYnJldml2aWRvIGVsIHBlcmlvZG8gYW50ZXJpb3IgJGstMSQuDQoNCkEgcGFydGlyIGRlIGVzdGFzIGVzdGltYWNpw7NuZXMgZGUgbGFzIHByb3BvcmNpb25lcyAkXGhhdHtwfV9rJCBwb2RlbW9zIGNhbGN1bGFyIGxhIHByb2JhYmlsaWRhZCBlc3RpbWFkYSBkZSBzb2JyZXZpdmlyIGVuIHVuIHRpZW1wbyAkdCQsIGxhIGN1YWwgZXN0w6EgZGFkYSBwb3I6DQoNCiQkXGhhdHtTfSh0KSA9IFxwcm9kX3tpID0gMX1ee3R9XGhhdHtwfV9pJCQNCg0KRXMgaW1wb3J0YW50ZSBtZW5jaW9uYXIgcXVlIHBvZGVtb3MgZW50ZW5kZXIgbGFzIHByb3BvcmNpb25lcyAkXGhhdHtwfV9rJCBjb21vIHByb2JhYmlsaWRhZGVzIGNvbmRpY2lvbmFsZXM6IGxhIHByb2JhYmlsaWRhZCBkZSBzb2JyZXZpdmlyIG90cm8gcGVyaW9kbyBkZSB0aWVtcG8gZGFkbyBxdWUgeWEgc29icmV2aXbDrSBsb3MgJChrLTEpJCBhbnRlcmlvcmVzLg0KDQojIyBFamVtcGxvLg0KDQpFc3RlIGVqZW1wbG8gZXMgcHJlc2VudGFkbyB5IGRlc2Fycm9sbGFkbyBlbiBbMl0uIEVuIGVzdGUgZWplbXBsbyAxNC4zLjEsIGhhY2llbmRvIHJlZmVyZW5jaWEgYWwgZXN0dWRpbyBkZSBNYXJ0aW5pIGV0IGFsIFsxXSwgc2UgYW5hbGl6YSBlbCB0aWVtcG8gZGUgc3VwZXJ2aXZlbmNpYSBkZSBwZXJzb25hcyBxdWUgdHV2aWVyb24gdHVtb3Jlcy4gRW4gZXNwZWPDrWZpY28sIHNlIHRpZW5lbiBkb3MgdGlwb3MgZGUgdHVtb3JlcyAoeSBkb3MgZGF0YXNldHMgZW4gY29uc2VjdWVuY2lhKSwgbG9zIGRlIGdyYWRvIGJham8gKGNvbiAyNSBwZXJzb25hcyBlbiBlc3RhIGNhdGVnb3JpYSkgeSBsb3MgZGUgZ3JhZG8gYWx0byAoY29uIDE0IHBlcnNvbmFzKS4gTnVlc3RybyBvYmpldGl2byBlcyBjb21wYXJhciBlbCB0aWVtcG8gZGUgc3VwZXJ2aXZlbmNpYSBlbnRyZSBhbWJvcyBncnVwb3MsIHBhcmEgbG8gcXVlIHVzYXJlbW9zIGVsIG3DqXRvZG8gZGUgS2FwbGFuLU1laWVyLg0KDQpQcmltZXJhbWVudGUsIGltcG9ydGFtb3MgeSBtb3N0cmFtb3MgbGEgZXN0cnVjdHVyYSBkZSBsb3MgZGF0b3M6IA0KDQpgYGB7cn0NCmRmIDwtIHJlYWQuY3N2KCdFWEFfQzE0X1MwM18wMS5jc3YnKQ0KaGVhZChkZikNCmBgYA0KUG9kZW1vcyBvYnNlcnZhciBxdWUgdGVuZW1vcyBpbmZvcm1hY2nDs24gZGVsIHRpZW1wbyBkZSBzdXBlcnZpdmVuY2lhLCB5IGVsIHRpcG8gZGUgdG9tb3IgKCpMKiDirpUgZ3JhZG8gYmFqbywgKkgq4q6VZyByYWRvIGFsdG8pLiBBZGVtw6FzLCB0ZW5lbW9zIHRyZXMgdGlwb3MgZGUgZXZlbnRvOiANCg0KLSAqZG9kKjogTXVlcnRlIHBvciBlbmZlcm1lZGFkIChkZWFkIG9mIGRpc2Vhc2UpLg0KLSAqbmVkKjogU2luIGV2aWRlbmNpYSBkZSBkZSBlbmZlcm1lZGFkIChubyBldmlkZW5jZSBvZiBkc2llYXNlKS4NCi0gKmRwbyo6IE11ZXJ0ZSBkZXNwdcOpcyBkZWwgZXN0dWRpbyAoZGVhZCBwb3N0b3BlcmF0aW9uKS4NCg0KQWhvcmEsIGVzIG5lY2VzYXJpbyBvcGVyYXIgZXN0b3MgZGF0b3MgcGFyYSBvYnRlbmVyIGxhcyBwcm9wb3JjaW9uZXMgJFxoYXR7cH1fayQsIGFzw60gY29tbyBsYSBmdW5jacOzbiBkZSBzdXBlcnZpdmVuY2lhICRcaGF0e1N9KHQpJC4NCg0KYGBge3J9DQojIFNlcGFyYW1vcyBwb3IgdGlwbyBkZSB0dW1vcg0KZGZfbCA8LSBkZlsgd2hpY2goZGYkVFVNT1I9PSdMJyksIF0NCmRmX2ggPC0gZGZbIHdoaWNoKGRmJFRVTU9SPT0nSCcpLCBdDQoNCiMgRnVuY2nDs24gcGFyYSBjb25vY2VyIGVzdGF0dXM6DQpnZXQuc3RhdHVzIDwtIGZ1bmN0aW9uKHgpew0KICBpZih4ID09ICduZWQnKXsNCiAgICByZXR1cm4oMCkNCiAgfQ0KICBlbHNlew0KICAgIHJldHVybigxKQ0KICB9DQp9DQoNCiMgRGVmaW5pbW9zIGZ1bmNpw7NuIHF1ZSByZWFsaXphIGthcGxhbi1tZWllcg0Ka2FwbGFuLm1laWVyIDwtIGZ1bmN0aW9uKGRmKXsNCiAgdGltZSA8LSBjKCkgIyBBcnJlZ2xvIHBhcmEgZ3VhcmRhciBwYXJ0ZSB0ZW1wb3JhbA0KICByaXNrIDwtIGMoKSAjIFBhY2llbnRlcyBlbiByaWVzZ28gKGF1biB2aXZvcykNCiAgYWxpdmUgPC0gYygpICMgUGFjaWVudGVzIHZpdm9zIGFsIGZpbmFsIGRlbCBwZXJpb2RvDQogIHByb3BvcnRpb24gPC0gYygpICMgUHJvcG9yY2lvbnMgKHZpdm9zL3JpZXNnbykNCiAgY3VtdWxhdGl2ZSA8LSBjKCkgIyBGdW5jacOzbiBjdW11bGF0aXZhIGRlIHByb3BvcmNpb25lcyBTKHQpDQogIA0KICAjIEludGVydmFsbyBkZSB0aWVtcG8gZGUgaW50ZXLDqXMNCiAgbWF4IDwtIG1heChkZiRUSU1FKQ0KICBtaW4gPC0gbWluKGRmJFRJTUUpDQogIA0KICAjIE7Dum1lcm8gdG90YWwgZGUgcGFjaWVudGVzDQogIG4gPC0gbGVuZ3RoKGRmJFNVQkopDQoNCiAgIyBPYnRlbmVtb3MgcHJvcG9yY2lvbmVzIGVuIG9yZGVuDQogIGZvciAoayBpbiBtaW46bWF4KXsNCiAgICANCiAgICAjIE9idGVuZW1vcyBwYWNpZW50ZXMgY29uIGVzZSB0aWVtcG8NCiAgICBhdXggPSBkZlt3aGljaChkZiRUSU1FPT1rKSxdDQogICAgDQogICAgIyBFbiBjYXNvIGRlIHRlbmVyIHBhY2llbnRlDQogICAgaWYobGVuZ3RoKGF1eCRWSVRBTCkgPiAwKXsNCiAgICAgICMgTGlzdGEgcGFyYSBlc3RhdHVzIGRlIHBhY2llbnRlcw0KICAgICAgc3RhdCA8LSBjKCkNCiAgICAgIGZvcihqIGluIDE6bGVuZ3RoKGF1eCRWSVRBTCkpew0KICAgICAgICBzdGF0IDwtIGFwcGVuZChzdGF0LCBnZXQuc3RhdHVzKGF1eCRWSVRBTFtqXSkpDQogICAgICB9DQogICAgICANCiAgICAgICMgU2kgc3VjZWRpw7MgYWxnbywgY2FsY3VsYW1vcyB0b2RvDQogICAgICBpZihpcy5lbGVtZW50KDEsIHN0YXQpKXsNCiAgICAgICAgdGltZSA8LSBhcHBlbmQodGltZSwgaykNCiAgICAgICAgZGllIDwtIHN1bShzdGF0ID09IDEpDQogICAgICAgIHN1cnZpdmUgPC0gc3VtKHN0YXQgPT0gMCkNCiAgICAgICAgDQogICAgICAgIHJpc2sgPC0gYXBwZW5kKHJpc2ssIG4pDQogICAgICAgIG4gPC0gbiAtIGRpZQ0KICAgICAgICBhbGl2ZSA8LSBhcHBlbmQoYWxpdmUsIG4pDQogICAgICAgIHByb3AgPC0gbi8obitkaWUpDQogICAgICAgIHByb3BvcnRpb248LSBhcHBlbmQocHJvcG9ydGlvbiwgcHJvcCkNCiAgICAgICAgaWYobGVuZ3RoKGN1bXVsYXRpdmUpID09IDApew0KICAgICAgICAgIGN1bXVsYXRpdmUgPC0gcHJvcG9ydGlvblsxXQ0KICAgICAgICB9DQogICAgICAgIGVsc2V7DQogICAgICAgICAgY3VtdWxhdGl2ZSA8LSBhcHBlbmQoY3VtdWxhdGl2ZSxwcm9wKmN1bXVsYXRpdmVbbGVuZ3RoKGN1bXVsYXRpdmUpXSkNCiAgICAgICAgfQ0KICAgICAgICBuIDwtIG4gLSBzdXJ2aXZlDQogICAgICB9DQogICAgICBlbHNlew0KICAgICAgICAjIFF1aXRhbW9zIHBhY2llbnRlcyBxdWUgbm8gbXVlcmVuDQogICAgICAgIG4gPC0gbi1sZW5ndGgoYXV4JFZJVEFMKQ0KICAgICAgfQ0KICAgIH0NCiAgDQogICAgfQ0KICBkZl9hbnMgPC0gZGF0YS5mcmFtZSh0aW1lLCByaXNrLCBhbGl2ZSwgcHJvcG9ydGlvbiwgY3VtdWxhdGl2ZSkNCiAgcmV0dXJuKGRmX2FucykNCn0NCiAgDQpgYGANCg0KQ29uIGVzdGEgZnVuY2nDs24sIG9idGVuZW1vcyBsYSBpbmZvcm1hY2nDs24gcmVsZXZhbnRlIHBhcmEgYW1ib3MgdHVtb3Jlcy4gUHJpbWVybywgcGFyYSBlbCBncmFkbyBiYWpvOg0KDQpgYGB7cn0NCmFuc19sIDwtIGthcGxhbi5tZWllcihkZl9sKQ0KYW5zX2wNCmBgYA0KDQpZIGFob3JhLCBlbCBncmFkbyBhbHRvOiANCg0KYGBge3J9DQphbnNfaCA8LSBrYXBsYW4ubWVpZXIoZGZfaCkNCmFuc19oDQpgYGANCg0KRmluYWxtZW50ZSwgZ3JhZmljYW1vczogDQoNCmBgYHtyfQ0KcGxvdCh4ID0gYXBwZW5kKGFuc19sJHRpbWUsIDIxMiksIHkgPSBhcHBlbmQoMSwgYW5zX2wkY3VtdWxhdGl2ZSksDQogICAgIHR5cGUgPSAnUycsDQogICAgIGNvbCA9ICdibHVlJywNCiAgICAgbWFpbiA9ICdDdXJ2YSBkZSBzdXBlcnZpdmVuY2lhIGRlIEthcGxhbi1NZWllcicsIA0KICAgICB4bGFiID0gJ1RpZW1wbyAobWVzZXMpJywNCiAgICAgeWxhYiA9ICdQcm9iYWJpbGlkYWQnLA0KICAgICB5bGltID0gYygwLCAxKSwgDQogICAgIHhsaW0gPSBjKDAsIDEyMCkpDQpsaW5lcyhhcHBlbmQoYW5zX2gkdGltZSwgMTQwKSwgYXBwZW5kKDEsIGFuc19oJGN1bXVsYXRpdmUpLCB0eXBlID0gJ1MnLCBjb2wgPSAncmVkJykNCg0KIyBFdGlxdWV0YXMgZGUgbMOtbmVhcy4NCmxlZ2VuZCgidG9wcmlnaHQiLCBjKCJHcmFkbyBiYWpvIiwgIkdyYWRvIGFsdG8iKSwgZmlsbD1jKCJibHVlIiwgInJlZCIpKQ0KYWJsaW5lKHYgPSA2MCkNCmFibGluZShoID0gMC41LCBjb2wgPSAnZ3JheScpDQoNCiMgQWdyZWdhbW9zIHVuYSBjdWFkcsOtY3VsYS4NCmdyaWQoKQ0KYGBgDQoNCkFsZ3VuYXMgbcOpdHJpY2FzIGltcG9ydGFudGVzOg0KDQoxLiAqTWVkaWFuYSBkZWwgdGllbXBvIGRlIHN1cGVydml2ZW5jaWE6KiBDb3JyZXNwb25kZSBjb24gZWwgcHVudG8gZG9uZGUgbGEgZnVuY2nDs24gY3VtdWxhdGl2YSBkZSBzdXBlcnZpdmVuY2lhIHRvZGEgJDAuNSQuDQoNCiAgICBFbiBlbCBjYXNvIGRlbCBncnVwbyBkZSBncmFkbyBiYWpvLCBlc3RvIGVzIDIxMiBtZXNlcywgeSBlbiBlbCBncnVwbyBkZSBncmFkbyBhbHRvIGVzIGRlIHRhbiBzb2xvIDkgbWVzZXMuDQoNCjIuICpQcm9wb3JjacOzbiBkZSBzdXBlcnZpdmVuY2lhIGEgNSBhw7Fvcyo6IE9ic2VydmFtb3MgYSBsYSBmdW5jacOzbiBhIGxvcyAxMiBhw7FvcyAoNjAgbWVzZXMpLg0KDQogICAgUGFyYSBlbCBncnVwbyBkZSBncmFkbyBiYWpvLCBlc3RvIGVzIGRlICQwLjczJCB5IGVsIGdyYWRvIGFsdG8gZXMgZGUgJDAuMDckLg0KDQozLiAqVGllbXBvIHByb21lZGlvIHNlIHN1cGVydml2ZW5jaWE6KiBDb3JyZXNwb25kZSBhIGRpdmlkaXIgZWwgdGllbXBvIHRvdGFsIGRlIHZpZGEgZW50cmUgdG9kb3MgbG9zIHN1amV0b3MgKHN1bWFkbyksIGRpdmlkaWRvIGVudHJlIGVsIG7Dum1lcm8gZGUgc3VqZXRvcy4NCg0KICAgIFBhcmEgZWwgZ3J1cG8gZGUgZ3JhZG8gYmFqbzoNCiAgICANCmBgYHtyfQ0KdG90YWwgPC0gc3VtKGRmX2wkVElNRSkNCnByaW50KHRvdGFsL2xlbmd0aChkZl9sJFZJVEFMKSkNCmBgYA0KICAgIA0KICAgIFkgcGFyYSBlbCBncnVwbyBkZSBncmFkbyBhbHRvOiANCiAgDQpgYGB7cn0NCnRvdGFsIDwtIHN1bShkZl9oJFRJTUUpDQpwcmludCh0b3RhbC9sZW5ndGgoZGZfaCRWSVRBTCkpDQpgYGANCg0KIyMgUmVmZXJlbmNpYXMuDQoNCjEuICoqTmFlbCBNYXJ0aW5pLCBBbmRyZXcgTWljaGFlbCBFLiBCdXJ0LCBSb2JlcnQgVC4gaGVlbGFuLCBNYW5qaXQgUy4gQmFpbnMsIFBhdHJpY2lhIE0uIE1jQ29ybWFjaywgVmxlcmllIFcuIFJzY2gsIE1pY2hhZWwgV2ViZXIsIFJvYmVydCBKLiBEb3duZXkgeSBSb2JlcnQgSi4gR2luc2JlcmcqKiwgKlByZWRpY3Rpb25zIG9mIFN1cnZpdmFsIGluIE1hbGlnbmFudCBUdW1vcnMgb2YgdGhlIFN0ZXJudW0qLCBKb3VybmFsIG9mIFRob3JhY2ljIGFuZCBDYXJkaW92YXN2dWxhciBTdXJnZXJ5LCAxMTEgKDE5OTYpLCA5NS0xMDYNCjIuICoqV2F5bmUgVy4gRGFuaWVsIHkgQ2hhZCBMLiBDcm9zcyoqLCAqQmlvc3RhdGlzdGljczogQSBGb3VuZGF0aW9uIGZvciBBbmFseXNpcyBpbiB0aGUgSGVhbHRoIFNjaWVuY2VzKiwgMTDCsCBlZGl0aW9uLCBXaWxleS4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQoNCiMjIEFuZXhvcy4NCg0KIyMjIERpYXBvc2l0aXZhcyBkZSBwcmVzZW50YWNpw7NuIGRlIHByb3llY3RvLiANCg0KU2UgcHVlZGVuIGVuY29udHJhciBlbiBlc3RlIFtsaW5rXShodHRwczovL2RvY3MuZ29vZ2xlLmNvbS9wcmVzZW50YXRpb24vZC9lLzJQQUNYLTF2VHdrN0lzMXdwSk0xc1RKT0VhY3NaMGN3VFY1VkktNGRVOEk5bldXSGl6bDF0NElRc2Rtd1ZPTnY4el9BLVNRYWRUWmRrYzlnQ2ZpeHI3L3B1Yj9zdGFydD1mYWxzZSZsb29wPWZhbHNlJmRlbGF5bXM9MTAwMDApLg0KDQo=