Introducción.

Para la realización de este proyecto, se pretende reproducir de manera parcial (de acuerdo a los datos disponibles) los resultados del artículo:

Giavarina D. Understanding Bland Altman analysis. Biochem Med (Zagreb). 2015;25(2):141-151. Published 2015 Jun 5. doi:10.11613/BM.2015.015


El análisis de diferencias: El método de Bland y Altman.

Primero, cargamos los datos y los visualizamos:


# Cargamos los datos
df <- read.csv('datos.csv')

# Visualizamos estructura del dataset
head(df, n = 8)

# Extraemos variables 
x <- df$Method.A
y <- df$Method.B

# Graficamos los datos
plot(x, y, type = 'p', 
     main = 'Scatter plot of measurements', 
     col = 'blue', xlab = 'Method A',
     ylab = 'Method B')

# Etiquetas de líneas.
legend("topleft", c("Data"), fill=c("blue"))

# Agregamos una cuadrícula.
grid()

Construimos una regresión lineal, para ver si desde ahí podemos observar si los métodos de medición están de acuerdo:

# Realizamos la regresion lineal
modelo <- lm(Method.B ~ Method.A, data = df)

# El resultado
summary(modelo)

Call:
lm(formula = Method.B ~ Method.A, data = df)

Residuals:
    Min      1Q  Median      3Q     Max 
-93.615 -11.908   0.052  12.314  55.504 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  12.0163     8.8667   1.355    0.186    
Method.A      1.0416     0.0181  57.561   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 32.49 on 28 degrees of freedom
Multiple R-squared:  0.9916,    Adjusted R-squared:  0.9913 
F-statistic:  3313 on 1 and 28 DF,  p-value: < 2.2e-16

Gráficamos este resultado de regresión con 99% de confianza:

# Definimos intervalo de interes
interval <- seq(min(x), max(x), length.out = 184)

# Calculamos las estimaciones para cada punto en el intervalo
mu <- predict(modelo, newdata = data.frame('Method A' = interval), 
              interval = "confidence", level = 0.99)

# Creamos gráfica
plot(x, y, type = 'p', 
     main = 'Confidence interval of 99% for linear regression', 
     col = 'blue', xlab = 'Method A',
     ylab = 'Method B')

# Coloreamos zona de confianza
polygon(c(rev(interval), interval),
        c(rev(mu[ ,3]), mu[ ,2]), col = 'grey',
        border = NA)

# Graficamos los datos
lines(x, y, type = 'p', col = 'blue')

# Graficamos mejor linea
abline(modelo)

# Etiquetas de líneas.
legend("topleft", c("Data"), fill=c("blue"))

# Agregamos una cuadrícula.
grid()

Ahora vamos a agregar las columnas necesarias al dataframe para poder construir las gráficas A&B. Primero, la columna de las diferencias:

df$diff <- df$Method.A - df$Method.B
head(df, 10)
NA

En teoría, si dos métodos estuvieran completamente de acuerdo, podríamos esperar que la diferencia entre sus mediciones sean siempre cere, y estén siempre de acuerdo). El problema de hacerlo de esta manera, es que no se considera el error de medición que pudiera existir en cada método.

Para considerar esto, debemos obtener el promedio de las diferencias de ambos métodos, ya que si la diferencia de cada medición es únicamente al error del método, entonces este promedio debería ser cercano a 0:

mean <- mean(df$diff)
mean
[1] -27.16667

En nuestro caso, el promedio de las diferencias no es cero, por lo que, en promedio, el método B mide \(27.17\) unidades más que el método A, en cada meidición. A esto le consideramos sesgo.

Si estamos seguros de que uno de los métodos está correcto, entonces con este promedio de diferencias podemos “ajustar” el otro método, pero en caso de que no haya ninguno que funcione como referencia, debemos investigar mas al respecto, obteniendo el promedio de las mediciones.

df$mean <- (df$Method.A + df$Method.B)/2
head(df, 8)

A partir de aquí, podemos construir la gráfica A&B, que es simplemente las diferencias entre los métodos contra el promedio entre los métodos. Esto nos permite investigar cualquier relación posible entre el error de medición y el valor real.

# Extraemos variables 
x <- df$mean
y <- df$diff

# Graficamos los datos
plot(x, y, type = 'p', 
     main = 'A&B plot', 
     col = 'blue', xlab = 'Mean of methods',
     ylab = 'Difference of methods')

abline(h = mean, col = 'red')

# Etiquetas de líneas.
legend("top", c("Data", "Mean (Bias)"), fill=c("blue", "red"))

# Agregamos una cuadrícula.
grid()

La línea roja, que corresponde con el promedio de las diferencias, es el bias respecto a la no diferencia entre los métodos. Se puede observar que las mediciones arriba de 200 son las responsables de este bias negativo.

Podemos hacer otra regresión lineal pero esta vez considerando estos ejes y datos, para encontrar alguna relación entre la diferencia de las mediciones y el valor real (que estamos estimando con el promedio de los métodos):

# Realizamos la regresion lineal
modelo <- lm(diff ~ mean, data = df)

# El resultado
summary(modelo)

Call:
lm(formula = diff ~ mean, data = df)

Residuals:
    Min      1Q  Median      3Q     Max 
-54.536 -12.483  -1.058  10.294  94.298 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)  
(Intercept) -10.14690    8.74881   -1.16   0.2559  
mean         -0.04505    0.01733   -2.60   0.0147 *
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 31.79 on 28 degrees of freedom
Multiple R-squared:  0.1945,    Adjusted R-squared:  0.1657 
F-statistic:  6.76 on 1 and 28 DF,  p-value: 0.01472

Visualizamos esta refresión con el intervalo de confianza al 0.95:

# Definimos intervalo de interes
interval <- seq(0, 1000, length.out = 184)

# Calculamos las estimaciones para cada punto en el intervalo
mu <- predict(modelo, newdata = data.frame("mean" = interval), 
              interval = "confidence", level = 0.95)

# Creamos gráfica
plot(x, y, type = 'p', 
     main = 'Confidence interval of 95% for linear regression \nin A&B plot', 
     col = 'blue', xlab = 'Mean of methods',
     ylab = 'Differences of methods')

# Coloreamos zona de confianza
polygon(c(rev(interval), interval),
        c(rev(mu[ ,3]), mu[ ,2]), col = 'grey',
        border = NA)

# Graficamos los datos
lines(x, y, type = 'p', col = 'blue')

# Graficamos mejor linea
abline(modelo)

# Gráfica de promedio (bias)
abline(h = mean, col = 'red')

# Etiquetas de líneas.
legend("top", c("Data", "Mean (Bias)"), fill=c("blue", "red"))

# Agregamos una cuadrícula.
grid()

Podemos ver que existe una tendencia negativa en la diferencia entre los métodos cuando el valor real estimado crece.

Nos interesa ahora obtener límites de acuerdo, esto es, un intervalo en el que podrémos decir que las diferencias entre las mediciones de ambos métodos se encuentran. Para esto, ya conocemos el promedio de las diferencias, obtenemos también la desviación estándar:

std <- sd(df$dif)
std
[1] 34.80595

Ahora consruimos un intervalo de confianza. Podríamos asumir que la distribución de la diferncia de los métodos es normal; sin embargo, debemos revisar que en efecto los datos sean normales. Para esto, se puede utilizar alguno de los múltiples test existentes. En este caso, mostraremos el histograma correspondiente y realizaremos una inspección visual y también con el test de Shapiro-Wilk:

differences <- df$diff
hist(differences)


shapiro.test(differences)

    Shapiro-Wilk normality test

data:  differences
W = 0.97956, p-value = 0.8137

Por lo tanto, podemos asumir que es normal (p = 0.814). Finalmente, construimos los intervalos de confianza al 95%:

up <- mean + 1.96*std
low <- mean - 1.96*std

print(paste(low, up))
[1] "-95.3863249384042 41.0529916050708"

Finalmente, nuestra gráfica A&B completa es:

# Extraemos variables 
x <- df$mean
y <- df$diff

# Graficamos los datos
plot(x, y, type = 'p', 
     main = 'A&B plot', 
     col = 'blue', xlab = 'Mean of methods',
     ylab = 'Difference of methods',
     ylim = c(-110, 50))

abline(h = mean, col = 'red')
abline(h = up, col = 'green')
abline(h = low, col = 'green')

# Etiquetas de líneas.
legend("top", c("Data", "Mean (Bias", "Limit"), fill=c("blue", "red", "green"))

# Agregamos una cuadrícula.
grid()

Por ultimo, es importante mencionar que el sistema de gráfica A&B no dice si el acuerdo es suficente, únicamente cuantifica el sesgo del rango de acuerdo, donde el 95% de las dfierences entre los métodos se encontrará. Se pueden realizar pruebas de significancia sobre este sesgo.

Referencias.

  1. Gallagher R, McKinley S, Dracup K. Predictors of women’s attendance at cardiac rehabilitation programs. Prog Cardiovasc Nurs. 2003 Summer;18(3):121-6. doi: 10.1111/j.0889-7204.2003.02129.x. PMID: 12893973.
  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.

LS0tDQp0aXRsZTogIlByb3llY3RvIDMgLSBFc3RhZMOtc3RpY2EgYXZhbnphZGEuIg0KYXV0aG9yOiAiR2FicmllbCBNaXNzYWVsIEJhcmNvLiBOVUE6IDQyNzA3MSB8IExhdXJhIGRlbCBDYXJtZW4gQ2FiYWwgUGFyYW1vLiBOVUE6IDM5MTU4MCINCmRhdGU6ICIxOCBkZSBOb3ZpZW1icmUgZGUgMjAyMSINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCjxzdHlsZT4NCmJvZHkge3RleHQtYWxpZ246IGp1c3RpZnk7IGZvbnQtc2l6ZTogMTJwdH0NCjwvc3R5bGU+DQoNCjxjZW50ZXI+DQogICAgPGltZyB3aWR0aD0iNjAlIiBzcmM9Imh0dHBzOi8vaS5pbWd1ci5jb20vVWFvOUNJeS5wbmciPg0KPC9jZW50ZXI+DQoNCiMjIEludHJvZHVjY2nDs24uDQoNClBhcmEgbGEgcmVhbGl6YWNpw7NuIGRlIGVzdGUgcHJveWVjdG8sIHNlIHByZXRlbmRlIHJlcHJvZHVjaXIgZGUgbWFuZXJhIHBhcmNpYWwgKGRlIGFjdWVyZG8gYSBsb3MgZGF0b3MgZGlzcG9uaWJsZXMpIGxvcyByZXN1bHRhZG9zIGRlbCBhcnTDrWN1bG86DQoNCj4gKipHaWF2YXJpbmEgRC4qKiBfVW5kZXJzdGFuZGluZyBCbGFuZCBBbHRtYW4gYW5hbHlzaXNfLiBCaW9jaGVtIE1lZCAoWmFncmViKS4gMjAxNTsyNSgyKToxNDEtMTUxLiBQdWJsaXNoZWQgMjAxNSBKdW4gNS4gZG9pOjEwLjExNjEzL0JNLjIwMTUuMDE1DQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgRWwgYW7DoWxpc2lzIGRlIGRpZmVyZW5jaWFzOiBFbCBtw6l0b2RvIGRlIEJsYW5kIHkgQWx0bWFuLg0KDQpQcmltZXJvLCBjYXJnYW1vcyBsb3MgZGF0b3MgeSBsb3MgdmlzdWFsaXphbW9zOiANCg0KYGBge3J9DQoNCiMgQ2FyZ2Ftb3MgbG9zIGRhdG9zDQpkZiA8LSByZWFkLmNzdignZGF0b3MuY3N2JykNCg0KIyBWaXN1YWxpemFtb3MgZXN0cnVjdHVyYSBkZWwgZGF0YXNldA0KaGVhZChkZiwgbiA9IDgpDQoNCiMgRXh0cmFlbW9zIHZhcmlhYmxlcyANCnggPC0gZGYkTWV0aG9kLkENCnkgPC0gZGYkTWV0aG9kLkINCg0KIyBHcmFmaWNhbW9zIGxvcyBkYXRvcw0KcGxvdCh4LCB5LCB0eXBlID0gJ3AnLCANCiAgICAgbWFpbiA9ICdTY2F0dGVyIHBsb3Qgb2YgbWVhc3VyZW1lbnRzJywgDQogICAgIGNvbCA9ICdibHVlJywgeGxhYiA9ICdNZXRob2QgQScsDQogICAgIHlsYWIgPSAnTWV0aG9kIEInKQ0KDQojIEV0aXF1ZXRhcyBkZSBsw61uZWFzLg0KbGVnZW5kKCJ0b3BsZWZ0IiwgYygiRGF0YSIpLCBmaWxsPWMoImJsdWUiKSkNCg0KIyBBZ3JlZ2Ftb3MgdW5hIGN1YWRyw61jdWxhLg0KZ3JpZCgpDQoNCmBgYA0KDQpDb25zdHJ1aW1vcyB1bmEgcmVncmVzacOzbiBsaW5lYWwsIHBhcmEgdmVyIHNpIGRlc2RlIGFow60gcG9kZW1vcyBvYnNlcnZhciBzaSBsb3MgbcOpdG9kb3MgZGUgbWVkaWNpw7NuIGVzdMOhbiBkZSBhY3VlcmRvOiANCg0KYGBge3J9DQojIFJlYWxpemFtb3MgbGEgcmVncmVzaW9uIGxpbmVhbA0KbW9kZWxvIDwtIGxtKE1ldGhvZC5CIH4gTWV0aG9kLkEsIGRhdGEgPSBkZikNCg0KIyBFbCByZXN1bHRhZG8NCnN1bW1hcnkobW9kZWxvKQ0KYGBgDQoNCkdyw6FmaWNhbW9zIGVzdGUgcmVzdWx0YWRvIGRlIHJlZ3Jlc2nDs24gY29uIDk5JSBkZSBjb25maWFuemE6IA0KDQpgYGB7cn0NCiMgRGVmaW5pbW9zIGludGVydmFsbyBkZSBpbnRlcmVzDQppbnRlcnZhbCA8LSBzZXEobWluKHgpLCBtYXgoeCksIGxlbmd0aC5vdXQgPSAxODQpDQoNCiMgQ2FsY3VsYW1vcyBsYXMgZXN0aW1hY2lvbmVzIHBhcmEgY2FkYSBwdW50byBlbiBlbCBpbnRlcnZhbG8NCm11IDwtIHByZWRpY3QobW9kZWxvLCBuZXdkYXRhID0gZGF0YS5mcmFtZSgnTWV0aG9kIEEnID0gaW50ZXJ2YWwpLCANCiAgICAgICAgICAgICAgaW50ZXJ2YWwgPSAiY29uZmlkZW5jZSIsIGxldmVsID0gMC45OSkNCg0KIyBDcmVhbW9zIGdyw6FmaWNhDQpwbG90KHgsIHksIHR5cGUgPSAncCcsIA0KICAgICBtYWluID0gJ0NvbmZpZGVuY2UgaW50ZXJ2YWwgb2YgOTklIGZvciBsaW5lYXIgcmVncmVzc2lvbicsIA0KICAgICBjb2wgPSAnYmx1ZScsIHhsYWIgPSAnTWV0aG9kIEEnLA0KICAgICB5bGFiID0gJ01ldGhvZCBCJykNCg0KIyBDb2xvcmVhbW9zIHpvbmEgZGUgY29uZmlhbnphDQpwb2x5Z29uKGMocmV2KGludGVydmFsKSwgaW50ZXJ2YWwpLA0KICAgICAgICBjKHJldihtdVsgLDNdKSwgbXVbICwyXSksIGNvbCA9ICdncmV5JywNCiAgICAgICAgYm9yZGVyID0gTkEpDQoNCiMgR3JhZmljYW1vcyBsb3MgZGF0b3MNCmxpbmVzKHgsIHksIHR5cGUgPSAncCcsIGNvbCA9ICdibHVlJykNCg0KIyBHcmFmaWNhbW9zIG1lam9yIGxpbmVhDQphYmxpbmUobW9kZWxvKQ0KDQojIEV0aXF1ZXRhcyBkZSBsw61uZWFzLg0KbGVnZW5kKCJ0b3BsZWZ0IiwgYygiRGF0YSIpLCBmaWxsPWMoImJsdWUiKSkNCg0KIyBBZ3JlZ2Ftb3MgdW5hIGN1YWRyw61jdWxhLg0KZ3JpZCgpDQpgYGANCg0KQWhvcmEgdmFtb3MgYSBhZ3JlZ2FyIGxhcyBjb2x1bW5hcyBuZWNlc2FyaWFzIGFsIGRhdGFmcmFtZSBwYXJhIHBvZGVyIGNvbnN0cnVpciBsYXMgZ3LDoWZpY2FzIEEmQi4gUHJpbWVybywgbGEgY29sdW1uYSBkZSBsYXMgZGlmZXJlbmNpYXM6IA0KDQpgYGB7cn0NCmRmJGRpZmYgPC0gZGYkTWV0aG9kLkEgLSBkZiRNZXRob2QuQg0KaGVhZChkZiwgMTApDQoNCmBgYA0KDQpFbiB0ZW9yw61hLCBzaSBkb3MgbcOpdG9kb3MgZXN0dXZpZXJhbiBjb21wbGV0YW1lbnRlIGRlIGFjdWVyZG8sIHBvZHLDrWFtb3MgZXNwZXJhciBxdWUgbGEgZGlmZXJlbmNpYSBlbnRyZSBzdXMgbWVkaWNpb25lcyBzZWFuIHNpZW1wcmUgY2VyZSwgeSBlc3TDqW4gc2llbXByZSBkZSBhY3VlcmRvKS4gRWwgcHJvYmxlbWEgZGUgaGFjZXJsbyBkZSBlc3RhIG1hbmVyYSwgZXMgcXVlIG5vIHNlIGNvbnNpZGVyYSBlbCBlcnJvciBkZSBtZWRpY2nDs24gcXVlIHB1ZGllcmEgZXhpc3RpciBlbiBjYWRhIG3DqXRvZG8uIA0KDQpQYXJhIGNvbnNpZGVyYXIgZXN0bywgZGViZW1vcyBvYnRlbmVyIGVsIHByb21lZGlvIGRlIGxhcyBkaWZlcmVuY2lhcyBkZSBhbWJvcyBtw6l0b2RvcywgeWEgcXVlIHNpIGxhIGRpZmVyZW5jaWEgZGUgY2FkYSBtZWRpY2nDs24gZXMgw7puaWNhbWVudGUgYWwgZXJyb3IgZGVsIG3DqXRvZG8sIGVudG9uY2VzIGVzdGUgcHJvbWVkaW8gZGViZXLDrWEgc2VyIGNlcmNhbm8gYSAwOg0KDQoNCmBgYHtyfQ0KbWVhbiA8LSBtZWFuKGRmJGRpZmYpDQptZWFuDQpgYGANCkVuIG51ZXN0cm8gY2FzbywgZWwgcHJvbWVkaW8gZGUgbGFzIGRpZmVyZW5jaWFzIG5vIGVzIGNlcm8sIHBvciBsbyBxdWUsIGVuIHByb21lZGlvLCBlbCBtw6l0b2RvIEIgbWlkZSAkMjcuMTckIHVuaWRhZGVzIG3DoXMgcXVlIGVsIG3DqXRvZG8gQSwgZW4gY2FkYSBtZWlkaWNpw7NuLiBBIGVzdG8gbGUgY29uc2lkZXJhbW9zIF9zZXNnb18uDQoNClNpIGVzdGFtb3Mgc2VndXJvcyBkZSBxdWUgdW5vIGRlIGxvcyBtw6l0b2RvcyBlc3TDoSBjb3JyZWN0bywgZW50b25jZXMgY29uIGVzdGUgcHJvbWVkaW8gZGUgZGlmZXJlbmNpYXMgcG9kZW1vcyAiYWp1c3RhciIgZWwgb3RybyBtw6l0b2RvLCBwZXJvIGVuIGNhc28gZGUgcXVlIG5vIGhheWEgbmluZ3VubyBxdWUgZnVuY2lvbmUgY29tbyByZWZlcmVuY2lhLCBkZWJlbW9zIGludmVzdGlnYXIgbWFzIGFsIHJlc3BlY3RvLCBvYnRlbmllbmRvIGVsIHByb21lZGlvIGRlIGxhcyBtZWRpY2lvbmVzLg0KDQpgYGB7cn0NCmRmJG1lYW4gPC0gKGRmJE1ldGhvZC5BICsgZGYkTWV0aG9kLkIpLzINCmhlYWQoZGYsIDgpDQpgYGANCg0KQSBwYXJ0aXIgZGUgYXF1w60sIHBvZGVtb3MgY29uc3RydWlyIGxhIGdyw6FmaWNhIEEmQiwgcXVlIGVzIHNpbXBsZW1lbnRlIGxhcyBkaWZlcmVuY2lhcyBlbnRyZSBsb3MgbcOpdG9kb3MgY29udHJhIGVsIHByb21lZGlvIGVudHJlIGxvcyBtw6l0b2Rvcy4gRXN0byBub3MgcGVybWl0ZSBpbnZlc3RpZ2FyIGN1YWxxdWllciByZWxhY2nDs24gcG9zaWJsZSBlbnRyZSBlbCBlcnJvciBkZSBtZWRpY2nDs24geSBlbCB2YWxvciByZWFsLg0KDQpgYGB7cn0NCiMgRXh0cmFlbW9zIHZhcmlhYmxlcyANCnggPC0gZGYkbWVhbg0KeSA8LSBkZiRkaWZmDQoNCiMgR3JhZmljYW1vcyBsb3MgZGF0b3MNCnBsb3QoeCwgeSwgdHlwZSA9ICdwJywgDQogICAgIG1haW4gPSAnQSZCIHBsb3QnLCANCiAgICAgY29sID0gJ2JsdWUnLCB4bGFiID0gJ01lYW4gb2YgbWV0aG9kcycsDQogICAgIHlsYWIgPSAnRGlmZmVyZW5jZSBvZiBtZXRob2RzJykNCg0KYWJsaW5lKGggPSBtZWFuLCBjb2wgPSAncmVkJykNCg0KIyBFdGlxdWV0YXMgZGUgbMOtbmVhcy4NCmxlZ2VuZCgidG9wIiwgYygiRGF0YSIsICJNZWFuIChCaWFzKSIpLCBmaWxsPWMoImJsdWUiLCAicmVkIikpDQoNCiMgQWdyZWdhbW9zIHVuYSBjdWFkcsOtY3VsYS4NCmdyaWQoKQ0KYGBgDQpMYSBsw61uZWEgcm9qYSwgcXVlIGNvcnJlc3BvbmRlIGNvbiBlbCBwcm9tZWRpbyBkZSBsYXMgZGlmZXJlbmNpYXMsIGVzIGVsIGJpYXMgcmVzcGVjdG8gYSBsYSBubyBkaWZlcmVuY2lhIGVudHJlIGxvcyBtw6l0b2Rvcy4gU2UgcHVlZGUgb2JzZXJ2YXIgcXVlIGxhcyBtZWRpY2lvbmVzIGFycmliYSBkZSAyMDAgc29uIGxhcyByZXNwb25zYWJsZXMgZGUgZXN0ZSBiaWFzIG5lZ2F0aXZvLg0KDQpQb2RlbW9zIGhhY2VyIG90cmEgcmVncmVzacOzbiBsaW5lYWwgcGVybyBlc3RhIHZleiBjb25zaWRlcmFuZG8gZXN0b3MgZWplcyB5IGRhdG9zLCBwYXJhIGVuY29udHJhciBhbGd1bmEgcmVsYWNpw7NuIGVudHJlIGxhIGRpZmVyZW5jaWEgZGUgbGFzIG1lZGljaW9uZXMgeSBlbCB2YWxvciByZWFsIChxdWUgZXN0YW1vcyBlc3RpbWFuZG8gY29uIGVsIHByb21lZGlvIGRlIGxvcyBtw6l0b2Rvcyk6DQoNCmBgYHtyfQ0KIyBSZWFsaXphbW9zIGxhIHJlZ3Jlc2lvbiBsaW5lYWwNCm1vZGVsbyA8LSBsbShkaWZmIH4gbWVhbiwgZGF0YSA9IGRmKQ0KDQojIEVsIHJlc3VsdGFkbw0Kc3VtbWFyeShtb2RlbG8pDQpgYGANClZpc3VhbGl6YW1vcyBlc3RhIHJlZnJlc2nDs24gY29uIGVsIGludGVydmFsbyBkZSBjb25maWFuemEgYWwgMC45NToNCg0KYGBge3J9DQojIERlZmluaW1vcyBpbnRlcnZhbG8gZGUgaW50ZXJlcw0KaW50ZXJ2YWwgPC0gc2VxKDAsIDEwMDAsIGxlbmd0aC5vdXQgPSAxODQpDQoNCiMgQ2FsY3VsYW1vcyBsYXMgZXN0aW1hY2lvbmVzIHBhcmEgY2FkYSBwdW50byBlbiBlbCBpbnRlcnZhbG8NCm11IDwtIHByZWRpY3QobW9kZWxvLCBuZXdkYXRhID0gZGF0YS5mcmFtZSgibWVhbiIgPSBpbnRlcnZhbCksIA0KICAgICAgICAgICAgICBpbnRlcnZhbCA9ICJjb25maWRlbmNlIiwgbGV2ZWwgPSAwLjk1KQ0KDQojIENyZWFtb3MgZ3LDoWZpY2ENCnBsb3QoeCwgeSwgdHlwZSA9ICdwJywgDQogICAgIG1haW4gPSAnQ29uZmlkZW5jZSBpbnRlcnZhbCBvZiA5NSUgZm9yIGxpbmVhciByZWdyZXNzaW9uIFxuaW4gQSZCIHBsb3QnLCANCiAgICAgY29sID0gJ2JsdWUnLCB4bGFiID0gJ01lYW4gb2YgbWV0aG9kcycsDQogICAgIHlsYWIgPSAnRGlmZmVyZW5jZXMgb2YgbWV0aG9kcycpDQoNCiMgQ29sb3JlYW1vcyB6b25hIGRlIGNvbmZpYW56YQ0KcG9seWdvbihjKHJldihpbnRlcnZhbCksIGludGVydmFsKSwNCiAgICAgICAgYyhyZXYobXVbICwzXSksIG11WyAsMl0pLCBjb2wgPSAnZ3JleScsDQogICAgICAgIGJvcmRlciA9IE5BKQ0KDQojIEdyYWZpY2Ftb3MgbG9zIGRhdG9zDQpsaW5lcyh4LCB5LCB0eXBlID0gJ3AnLCBjb2wgPSAnYmx1ZScpDQoNCiMgR3JhZmljYW1vcyBtZWpvciBsaW5lYQ0KYWJsaW5lKG1vZGVsbykNCg0KIyBHcsOhZmljYSBkZSBwcm9tZWRpbyAoYmlhcykNCmFibGluZShoID0gbWVhbiwgY29sID0gJ3JlZCcpDQoNCiMgRXRpcXVldGFzIGRlIGzDrW5lYXMuDQpsZWdlbmQoInRvcCIsIGMoIkRhdGEiLCAiTWVhbiAoQmlhcykiKSwgZmlsbD1jKCJibHVlIiwgInJlZCIpKQ0KDQojIEFncmVnYW1vcyB1bmEgY3VhZHLDrWN1bGEuDQpncmlkKCkNCmBgYA0KUG9kZW1vcyB2ZXIgcXVlIGV4aXN0ZSB1bmEgdGVuZGVuY2lhIG5lZ2F0aXZhIGVuIGxhIGRpZmVyZW5jaWEgZW50cmUgbG9zIG3DqXRvZG9zIGN1YW5kbyBlbCB2YWxvciByZWFsIGVzdGltYWRvIGNyZWNlLg0KDQpOb3MgaW50ZXJlc2EgYWhvcmEgb2J0ZW5lciBsw61taXRlcyBkZSBhY3VlcmRvLCBlc3RvIGVzLCB1biBpbnRlcnZhbG8gZW4gZWwgcXVlIHBvZHLDqW1vcyBkZWNpciBxdWUgbGFzIGRpZmVyZW5jaWFzIGVudHJlIGxhcyBtZWRpY2lvbmVzIGRlIGFtYm9zIG3DqXRvZG9zIHNlIGVuY3VlbnRyYW4uIFBhcmEgZXN0bywgeWEgY29ub2NlbW9zIGVsIHByb21lZGlvIGRlIGxhcyBkaWZlcmVuY2lhcywgb2J0ZW5lbW9zIHRhbWJpw6luIGxhIGRlc3ZpYWNpw7NuIGVzdMOhbmRhcjogDQoNCmBgYHtyfQ0Kc3RkIDwtIHNkKGRmJGRpZikNCnN0ZA0KYGBgDQpBaG9yYSBjb25zcnVpbW9zIHVuIGludGVydmFsbyBkZSBjb25maWFuemEuIFBvZHLDrWFtb3MgYXN1bWlyIHF1ZSBsYSBkaXN0cmlidWNpw7NuIGRlIGxhIGRpZmVybmNpYSBkZSBsb3MgbcOpdG9kb3MgZXMgbm9ybWFsOyBzaW4gZW1iYXJnbywgZGViZW1vcyByZXZpc2FyIHF1ZSBlbiBlZmVjdG8gbG9zIGRhdG9zIHNlYW4gbm9ybWFsZXMuIFBhcmEgZXN0bywgc2UgcHVlZGUgdXRpbGl6YXIgYWxndW5vIGRlIGxvcyBtw7psdGlwbGVzIHRlc3QgZXhpc3RlbnRlcy4gRW4gZXN0ZSBjYXNvLCBtb3N0cmFyZW1vcyBlbCBoaXN0b2dyYW1hIGNvcnJlc3BvbmRpZW50ZSB5IHJlYWxpemFyZW1vcyB1bmEgaW5zcGVjY2nDs24gdmlzdWFsIHkgdGFtYmnDqW4gY29uIGVsIHRlc3QgZGUgU2hhcGlyby1XaWxrOiANCg0KYGBge3J9DQpkaWZmZXJlbmNlcyA8LSBkZiRkaWZmDQpoaXN0KGRpZmZlcmVuY2VzKQ0KDQpzaGFwaXJvLnRlc3QoZGlmZmVyZW5jZXMpDQpgYGANCg0KUG9yIGxvIHRhbnRvLCBwb2RlbW9zIGFzdW1pciBxdWUgZXMgbm9ybWFsIChwID0gMC44MTQpLiBGaW5hbG1lbnRlLCBjb25zdHJ1aW1vcyBsb3MgaW50ZXJ2YWxvcyBkZSBjb25maWFuemEgYWwgOTUlOg0KDQpgYGB7cn0NCnVwIDwtIG1lYW4gKyAxLjk2KnN0ZA0KbG93IDwtIG1lYW4gLSAxLjk2KnN0ZA0KDQpwcmludChwYXN0ZShsb3csIHVwKSkNCmBgYA0KRmluYWxtZW50ZSwgbnVlc3RyYSBncsOhZmljYSBBJkIgY29tcGxldGEgZXM6IA0KDQpgYGB7cn0NCiMgRXh0cmFlbW9zIHZhcmlhYmxlcyANCnggPC0gZGYkbWVhbg0KeSA8LSBkZiRkaWZmDQoNCiMgR3JhZmljYW1vcyBsb3MgZGF0b3MNCnBsb3QoeCwgeSwgdHlwZSA9ICdwJywgDQogICAgIG1haW4gPSAnQSZCIHBsb3QnLCANCiAgICAgY29sID0gJ2JsdWUnLCB4bGFiID0gJ01lYW4gb2YgbWV0aG9kcycsDQogICAgIHlsYWIgPSAnRGlmZmVyZW5jZSBvZiBtZXRob2RzJywNCiAgICAgeWxpbSA9IGMoLTExMCwgNTApKQ0KDQphYmxpbmUoaCA9IG1lYW4sIGNvbCA9ICdyZWQnKQ0KYWJsaW5lKGggPSB1cCwgY29sID0gJ2dyZWVuJykNCmFibGluZShoID0gbG93LCBjb2wgPSAnZ3JlZW4nKQ0KDQojIEV0aXF1ZXRhcyBkZSBsw61uZWFzLg0KbGVnZW5kKCJ0b3AiLCBjKCJEYXRhIiwgIk1lYW4gKEJpYXMiLCAiTGltaXQiKSwgZmlsbD1jKCJibHVlIiwgInJlZCIsICJncmVlbiIpKQ0KDQojIEFncmVnYW1vcyB1bmEgY3VhZHLDrWN1bGEuDQpncmlkKCkNCmBgYA0KDQpQb3IgdWx0aW1vLCBlcyBpbXBvcnRhbnRlIG1lbmNpb25hciBxdWUgZWwgc2lzdGVtYSBkZSBncsOhZmljYSBBJkIgbm8gZGljZSBzaSBlbCBhY3VlcmRvIGVzIHN1ZmljZW50ZSwgw7puaWNhbWVudGUgY3VhbnRpZmljYSBlbCBzZXNnbyBkZWwgcmFuZ28gZGUgYWN1ZXJkbywgZG9uZGUgZWwgOTUlIGRlIGxhcyBkZmllcmVuY2VzIGVudHJlIGxvcyBtw6l0b2RvcyBzZSBlbmNvbnRyYXLDoS4gU2UgcHVlZGVuIHJlYWxpemFyIHBydWViYXMgZGUgc2lnbmlmaWNhbmNpYSBzb2JyZSBlc3RlIHNlc2dvLg0KDQojIyBSZWZlcmVuY2lhcy4NCg0KMS4gKipHYWxsYWdoZXIgUiwgTWNLaW5sZXkgUywgRHJhY3VwIEsqKi4gKlByZWRpY3RvcnMgb2Ygd29tZW4ncyBhdHRlbmRhbmNlIGF0IGNhcmRpYWMgcmVoYWJpbGl0YXRpb24gcHJvZ3JhbXMqLiBQcm9nIENhcmRpb3Zhc2MgTnVycy4gMjAwMyBTdW1tZXI7MTgoMyk6MTIxLTYuIGRvaTogMTAuMTExMS9qLjA4ODktNzIwNC4yMDAzLjAyMTI5LnguIFBNSUQ6IDEyODkzOTczLg0KMi4gKipXYXluZSBXLiBEYW5pZWwgeSBDaGFkIEwuIENyb3NzKiosICpCaW9zdGF0aXN0aWNzOiBBIEZvdW5kYXRpb24gZm9yIEFuYWx5c2lzIGluIHRoZSBIZWFsdGggU2NpZW5jZXMqLCAxMMKwIGVkaXRpb24sIFdpbGV5Lg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIEFuZXhvcy4NCg0KIyMjIERpYXBvc2l0aXZhcyBkZSBwcmVzZW50YWNpw7NuIGRlIHByb3llY3RvLiANCg0KU2UgcHVlZGVuIGVuY29udHJhciBlbiBlc3RlIFtsaW5rXShodHRwczovL2RvY3MuZ29vZ2xlLmNvbS9wcmVzZW50YXRpb24vZC9lLzJQQUNYLTF2UVVhbFV5RHg1UnlsRk1Yc0dqNXduQkhMUzBCOXRlRFJ0d01QVjhWZXpmTUpwQ19ZYjJkdmZmdHJuWUVIdEVHZ1plTEV1alNadGR0NENHL3B1Yj9zdGFydD1mYWxzZSZsb29wPWZhbHNlJmRlbGF5bXM9NjAwMDApLg0KDQo=