Objectif de l’étude

Cette étude vise à modéliser le rythme et l’intensité des épidémies de gastro-entérite au cours de la dernière décennie afin de prédire leur évolution à court et moyen terme. Cet exercice de modélisation peut être utile aux autorités sanitaires, aux établissements et professionnels de santé, aux assurances, etc. afin de planifier leur activité.

Données

Nous recourons aux données de l’Indicateur Avancé Sanitaire (IAS) mises à disposition par OpenHealth Company sur le site d’ouverture des données publiques data.gouv.fr. Elles sont accessibles à l’adresse suivante : https://www.data.gouv.fr/fr/datasets/indicateur-avance-sanitaire-ias-gastro-enterite/

Nous choisissons le 1er janvier 2010 comme point de départ de la série longitudinale, l’année 2009 comprenant des valeurs manquantes. De plus, si le jeu de données initial fournit des données quotidiennes, nous ne retenons pour cette étude qu’un jour par semaine, en l’occurrence le vendredi, car certaines modélisations ne sont pas possibles sur des données dont la fréquence est trop importante.

# required libraries
library(readr)
library(tidyverse)
library(ggplot2)
library(forecast)
library(seasonal)
library(fracdiff)
Gastro <- read_delim("OpenHealth_Gastro.csv", ";", escape_double = FALSE, 
                     locale = locale(decimal_mark = ",", encoding = "ISO-8859-1"), trim_ws = TRUE)
names(Gastro) <- c('PERIODE', 'IAS') # names of the dataset
Gastro <- Gastro[-c(1:365), ] # remove the year 2009
Gastro$PERIODE <- weekdays(as.POSIXct(Gastro$PERIODE), abbreviate = F) # change the date syntax format to weekdays
Gastro_reduced <- dplyr::filter(Gastro, Gastro$PERIODE == 'vendredi') # keep only this weekday
Gastro_ts_weeks <- ts(Gastro_reduced[, 2], start = 2010, frequency = 52) # make a ts object

Les épidémies de gastro-entérite depuis 2010

Les graphiques suivants montrent l’évolution de l’indice des gastro-entérites en France depuis 2010. Sans surprise, les pics d’épidémies surviennent en général dans les premières et les dernières semaines de chaque année.

Les corrélogrammes mettent en évidence les coefficients d’autocorrélation de la série. La plupart sont significativement différents de 0, ce qui dénote bien de la saisonnalité de la série.

# time series since 2010
autoplot(Gastro_ts_weeks) +
        labs(title = "Epidémies de gastro-entérite depuis 2010", 
             x = "Année", y = "Indicateur Avancé Sanitaire")

# seasonality
ggseasonplot(Gastro_ts_weeks) +
        labs(title = "Episodes de gastro-entérite par année", x = "Semaine", y = "IAS")

ggseasonplot(Gastro_ts_weeks, polar = TRUE) +
        labs(title = "Episodes de gastro-entérite par année", x = "Semaine", y = "IAS")

# ACF and PACF
ggtsdisplay(Gastro_ts_weeks)

Modélisations et prédictions des épidémies de gastro-entérite pour les années 2019-2020

La prédiction sur la base de séries longitudinales étant toujours un exercice très incertain, il convient de recourir à plusieurs techniques afin de comparer leurs qualités et défauts respectifs. A noter que dans les graphiques qui suivent, des intervalles de confiance à 80 % et 95 % sont affichés en couleur afin de rendre compte du degré d’incertitude de la projection.

Prédictions à partir de la décomposition de la saisonnalité

A l’aide d’une décomposition de type STL (Seasonal and trend decomposition using loess), il est possible de faire apparaître la tendance de long terme de la série ainsi que sa saisonnalité. Conformément aux corrélogrammes vus précédemment, la série comporte une saisonnalité très marquée (en gris) mais la tendance lourde (en rouge) est relativement stable, au moins depuis 2015.

Le niveau prédit par le modèle pour les années 2019 et 2020 parait faible en ce qui concerne les semaines qui ne connaissent pas de forts pics d’épidémies.

Gastro_ts_weeks[,1] %>% mstl(robust=TRUE) -> fit_mSTL
autoplot(Gastro_ts_weeks, series="Données brutes") +
  autolayer(trendcycle(fit_mSTL), series="Tendance") +
  autolayer(seasadj(fit_mSTL), series="Ajustement de la saisonnalité") +
  xlab("Année") + ylab("IAS") +
  ggtitle("Décomposition des épidémies de gastro-entérite depuis 2010") +
  scale_colour_manual(values=c("gray","blue","red"),
             breaks=c("Données brutes","Ajustement de la saisonnalité","Tendance"))

fit_mSTL %>% forecast(method="naive", h = 104) %>%
  autoplot() + ylab("IAS") + xlab("Année") + ggtitle("Prédictions des épidémies pour 2019-2020")

Modèle ETS

Le modèle ETS (Error, Trend, Seasonal) appartient à la famille du lissage exponentiel, qui pondère les observations passées de manière de plus en plus forte à mesure que les observations sont récentes.

En l’occurrence, le modèle ETS n’est pas un bon choix de modélisation, comme le montre l’erreur élevée (AICc = 4467) et l’amplitude des intervalles de confiance. Le niveau prédit par le modèle pour les années 2019 et 2020 parait de nouveau faible en ce qui concerne les semaines qui ne connaissent pas de forts pics d’épidémies ; ce résultat est logique puisque le modèle ETS s’est appuyé sur la même méthode de décomposition STL.

ets_model <- forecast(Gastro_ts_weeks, h = 104)
summary(ets_model)

Forecast method: STL +  ETS(A,N,N)

Model Information:
ETS(A,N,N) 

Call:
 ets(y = x, model = etsmodel, allow.multiplicative.trend = allow.multiplicative.trend) 

  Smoothing parameters:
    alpha = 0.9998 

  Initial states:
    l = 118.0236 

  sigma:  5.0912

     AIC     AICc      BIC 
4467.287 4467.338 4479.771 

Error measures:
                      ME     RMSE      MAE        MPE     MAPE     MASE         ACF1
Training set -0.04010455 5.080428 2.813324 -0.1000158 2.225832 0.293453 9.292575e-05

Forecasts:
autoplot(ets_model) +
        labs(title = "Prédictions des épidémies de gastro-entérite pour les années 2019-2020",
             x = "Année", y = "IAS")

Modèle ARIMA

Au contraire des modèles de lissage exponentiel, tels qu’ETS, les modèles ARIMA recherchent les coefficients d’autocorrélation de la série.

En l’occurrence, puisque l’indice de gastro-entérite connait une forte saisonnalité, il est nécessaire de différencier au moins une fois la série que nous étudions afin de la rendre davantage stationnaire. Les prédictions issues du modèle semblent crédibles concernant les pics d’épidémie, mais le niveau de l’indice parait faible pour les semaines hors pics d’épidémie.

modelARIMA_1 <- auto.arima(Gastro_ts_weeks, d = 1, approximation=FALSE,trace=FALSE) # d=1 means we differentiate once
summary(modelARIMA_1) # ARIMA(1,1,1)(0,1,2)[52] with zero mean. AIcc = 2747.76
Series: Gastro_ts_weeks 
ARIMA(1,1,1)(0,1,2)[52] 

Coefficients:
          ar1     ma1     sma1    sma2
      -0.4974  0.5905  -0.4468  0.1697
s.e.   0.2459  0.2258   0.0523  0.0548

sigma^2 estimated as 38.38:  log likelihood=-1368.81
AIC=2747.61   AICc=2747.76   BIC=2767.83

Training set error measures:
                      ME    RMSE      MAE       MPE     MAPE      MASE        ACF1
Training set -0.09372274 5.81095 3.113939 -0.105925 2.467264 0.3248096 -0.02173083
modelARIMA_1 %>% forecast(h = 104) %>% autoplot() + 
        labs(title = "Prédictions des épidémies de gastro-entérite pour 2019-2020",
             x = "Année", y = "Indice")

Réseau de neurones

Les réseaux de neurones demeurent relativement peu utilisés dans le cas de séries longitudinales. Le modèle retenu ici possède deux neurones dans l’unique couche cachée. Ses prédictions pour 2019 et 2020 semblent crédibles pour les semaines hors pics d’épidémie, en comparaison des projections des précédents modèles. En revanche, il tend probablement à sous-estimer l’ampleur des pics d’épidémie.

# fit the model
fitNN <- nnetar(Gastro_ts_weeks, lambda = 0)
summary(fitNN)
          Length Class        Mode     
x         474    ts           numeric  
m           1    -none-       numeric  
p           1    -none-       numeric  
P           1    -none-       numeric  
scalex      2    -none-       list     
size        1    -none-       numeric  
lambda      1    -none-       numeric  
subset    474    -none-       numeric  
model      20    nnetarmodels list     
nnetargs    0    -none-       list     
fitted    474    ts           numeric  
residuals 474    ts           numeric  
lags        3    -none-       numeric  
series      1    -none-       character
method      1    -none-       character
call        3    -none-       call     
# plot the model's forecasts
autoplot(forecast(fitNN, h = 104))

# prediction intervals
# compute prediction intervals using simulation where future sample paths are generated using bootstrapped residuals
sim <- ts(matrix(0, nrow=2L, ncol=6L), start = end(Gastro_ts_weeks)[1L]+1L)
for(i in seq(6)) {
        sim[,i] <- simulate(fitNN, nsim=2L)
        }
        # plot the prediction Intervals for the fitNN model 
fcast <- forecast(fitNN, PI = TRUE, h = 104)
autoplot(fcast) +
        labs(title = "Prédictions des épidémies de gastro-entérite pour 2019-2020 avec un réseau de neurones", 
             x = "Année", y = "Indice")

Termes de Fourier

Il est également possible de recourir aux termes de Fourier comme variable prédictive de la saisonnalité d’un modèle ARIMA. K désigne le nombre de paires de sinus et de cosinus. Il est choisi à l’issue d’un arbitrage entre la réduction de l’erreur (ici mesurée par l’AICc) et le degré de flexibilité pour éviter le sur-apprentissage (overfitting).

Après avoir essayé 6 différentes valeurs pour K, nous retenons K=4, soit 4 paires de sinus et de cosinus, car le gain marginal associé à la 4e paire est forte en termes de réduction de l’AICc tandis que la 5e paire n’apporte que peu de bénéfices au prix d’une flexibilité potentiellement trop forte.

Les prédictions de ce modèle pour les années 2019 et 2020 semblent trop faibles en ce qui concerne les semaines qui ne connaissent pas de forts pics d’épidémie. En revanche les semaines pour lesquelles les épidémies sont nombreuses paraissent mieux approximées.

# Fourier 1/2
plots <- list()
for (i in seq(6)) {
  fit <- auto.arima(Gastro_ts_weeks, xreg = fourier(Gastro_ts_weeks, K = i), seasonal = TRUE, lambda = 0)
  plots[[i]] <- autoplot(forecast(fit, xreg=fourier(Gastro_ts_weeks, K=i, h=104))) +
    xlab(paste("K=",i,"AICC=",round(fit[["aicc"]],2))) +
    ylab("")
}
gridExtra::grid.arrange( #from package gridExtra
  plots[[1]],plots[[2]],plots[[3]],
  plots[[4]],plots[[5]],plots[[6]], nrow=3) 

# Keep K = 4 model because beyond the reduction in AICc is weak
# Fourier 2/2
fourier_Gastro <- auto.arima(Gastro_ts_weeks, 
                             xreg = fourier(Gastro_ts_weeks, K = 4), seasonal = TRUE, lambda = 0) # K = 4, cf graphs
summary(fourier_Gastro) # Regression with ARIMA(2,1,1)(0,0,1)[52]. AICc=-1633.62
Series: Gastro_ts_weeks 
Regression with ARIMA(2,1,1)(0,0,1)[52] errors 
Box Cox transformation: lambda= 0 

Coefficients:
         ar1      ar2      ma1    sma1   S1-52   C1-52   S2-52   C2-52   S3-52   C3-52   S4-52   C4-52
      0.5448  -0.3274  -0.4484  0.3835  0.0751  0.1495  0.1096  0.0512  0.0116  0.0250  0.0505  0.0162
s.e.  0.2071   0.0451   0.2277  0.0377  0.0215  0.0216  0.0114  0.0114  0.0082  0.0082  0.0069  0.0069

sigma^2 estimated as 0.001763:  log likelihood=830.21
AIC=-1634.41   AICc=-1633.62   BIC=-1580.34

Training set error measures:
                      ME     RMSE      MAE        MPE    MAPE      MASE        ACF1
Training set -0.08852325 6.631208 3.520882 -0.1865732 2.73298 0.3672572 -0.06039594
autoplot(forecast(fourier_Gastro, xreg=fourier(Gastro_ts_weeks, K=4, h=104))) +
        labs(title = "Prédictions des épidémies de gastro-entérite pour 2019-2020 avec les termes de Fourier", 
             x = "Année", y = "Indice")

Conclusion

Différentes méthodes ont été utilisées afin de prédire les épidémies de gastro-entérite des années 2019 et 2020. La plupart des modèles tendent à prédire un niveau faible de l’indice pour les semaines hors pics d’épidémie, ce qui parait douteux. Pour ces semaines, les prédictions du réseau de neurones paraissent les plus crédibles. En revanche, le réseau de neurones semble sous-estimer l’ampleur des pics d’épidémies, lesquels pourraient être mieux approximés notamment par les modèles ETS, ARIMA voire avec les termes de Fourier.

Enfin, il convient de relever la grande incertitude autour de ces projections au regard de l’étendue des intervalles de confiance (surtout pour 2020, c’est-à-dire à un horizon plus lointain), hormis pour le réseau de neurones et, dans une certaine mesure, les termes de Fourier.

Il serait opportun de calculer, à la fin de l’année 2019, l’erreur moyenne commise par les différents modèles au regard des valeurs observées, afin d’obtenir de meilleures prédictions pour 2020.

LS0tDQp0aXRsZTogIkxlcyDpcGlk6W1pZXMgZGUgZ2FzdHJvLWVudOlyaXRlIg0KYXV0aG9yOiBPbGl2aWVyIFZpb2xsZXQgLSBvbGl2aWVyLnZpb2xsZXRbYV1nbWFpbC5jb20NCmRhdGU6IEphbnZpZXIgMjAxOQ0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KIyMgT2JqZWN0aWYgZGUgbCfpdHVkZQ0KDQpDZXR0ZSDpdHVkZSB2aXNlIOAgbW9k6Wxpc2VyIGxlIHJ5dGhtZSBldCBsJ2ludGVuc2l06SBkZXMg6XBpZOltaWVzIGRlIGdhc3Ryby1lbnTpcml0ZSBhdSBjb3VycyBkZSBsYSBkZXJuaehyZSBk6WNlbm5pZSBhZmluIGRlIHBy6WRpcmUgbGV1ciDpdm9sdXRpb24g4CBjb3VydCBldCBtb3llbiB0ZXJtZS4gQ2V0IGV4ZXJjaWNlIGRlIG1vZOlsaXNhdGlvbiBwZXV0IOp0cmUgdXRpbGUgYXV4IGF1dG9yaXTpcyBzYW5pdGFpcmVzLCBhdXgg6XRhYmxpc3NlbWVudHMgZXQgcHJvZmVzc2lvbm5lbHMgZGUgc2FudOksIGF1eCBhc3N1cmFuY2VzLCBldGMuIGFmaW4gZGUgcGxhbmlmaWVyIGxldXIgYWN0aXZpdOkuIA0KDQojIyMgRG9ubullcw0KDQpOb3VzIHJlY291cm9ucyBhdXggZG9ubullcyBkZSBsJyoqSW5kaWNhdGV1ciBBdmFuY+kgU2FuaXRhaXJlKiogKElBUykgbWlzZXMg4CBkaXNwb3NpdGlvbiBwYXIgKipPcGVuSGVhbHRoIENvbXBhbnkqKiBzdXIgbGUgc2l0ZSBkJ291dmVydHVyZSBkZXMgZG9ubullcyBwdWJsaXF1ZXMgZGF0YS5nb3V2LmZyLiBFbGxlcyBzb250IGFjY2Vzc2libGVzIOAgbCdhZHJlc3NlIHN1aXZhbnRlIDogaHR0cHM6Ly93d3cuZGF0YS5nb3V2LmZyL2ZyL2RhdGFzZXRzL2luZGljYXRldXItYXZhbmNlLXNhbml0YWlyZS1pYXMtZ2FzdHJvLWVudGVyaXRlLyANCg0KTm91cyBjaG9pc2lzc29ucyBsZSAqKjFlciBqYW52aWVyIDIwMTAqKiBjb21tZSBwb2ludCBkZSBk6XBhcnQgZGUgbGEgc+lyaWUgbG9uZ2l0dWRpbmFsZSwgbCdhbm7pZSAyMDA5IGNvbXByZW5hbnQgZGVzIHZhbGV1cnMgbWFucXVhbnRlcy4gRGUgcGx1cywgc2kgbGUgamV1IGRlIGRvbm7pZXMgaW5pdGlhbCBmb3Vybml0IGRlcyBkb25u6WVzIHF1b3RpZGllbm5lcywgbm91cyBuZSByZXRlbm9ucyBwb3VyIGNldHRlIOl0dWRlICoqcXUndW4gam91ciBwYXIgc2VtYWluZSoqLCBlbiBsJ29jY3VycmVuY2UgbGUgdmVuZHJlZGksIGNhciBjZXJ0YWluZXMgbW9k6Wxpc2F0aW9ucyBuZSBzb250IHBhcyBwb3NzaWJsZXMgc3VyIGRlcyBkb25u6WVzIGRvbnQgbGEgZnLpcXVlbmNlIGVzdCB0cm9wIGltcG9ydGFudGUuDQoNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgcmVxdWlyZWQgbGlicmFyaWVzDQoNCmxpYnJhcnkocmVhZHIpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZm9yZWNhc3QpDQpsaWJyYXJ5KHNlYXNvbmFsKQ0KbGlicmFyeShmcmFjZGlmZikNCmBgYA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KR2FzdHJvIDwtIHJlYWRfZGVsaW0oIk9wZW5IZWFsdGhfR2FzdHJvLmNzdiIsICI7IiwgZXNjYXBlX2RvdWJsZSA9IEZBTFNFLCANCiAgICAgICAgICAgICAgICAgICAgIGxvY2FsZSA9IGxvY2FsZShkZWNpbWFsX21hcmsgPSAiLCIsIGVuY29kaW5nID0gIklTTy04ODU5LTEiKSwgdHJpbV93cyA9IFRSVUUpDQoNCm5hbWVzKEdhc3RybykgPC0gYygnUEVSSU9ERScsICdJQVMnKSAjIG5hbWVzIG9mIHRoZSBkYXRhc2V0DQpHYXN0cm8gPC0gR2FzdHJvWy1jKDE6MzY1KSwgXSAjIHJlbW92ZSB0aGUgeWVhciAyMDA5DQpHYXN0cm8kUEVSSU9ERSA8LSB3ZWVrZGF5cyhhcy5QT1NJWGN0KEdhc3RybyRQRVJJT0RFKSwgYWJicmV2aWF0ZSA9IEYpICMgY2hhbmdlIHRoZSBkYXRlIHN5bnRheCBmb3JtYXQgdG8gd2Vla2RheXMNCkdhc3Ryb19yZWR1Y2VkIDwtIGRwbHlyOjpmaWx0ZXIoR2FzdHJvLCBHYXN0cm8kUEVSSU9ERSA9PSAndmVuZHJlZGknKSAjIGtlZXAgb25seSB0aGlzIHdlZWtkYXkNCkdhc3Ryb190c193ZWVrcyA8LSB0cyhHYXN0cm9fcmVkdWNlZFssIDJdLCBzdGFydCA9IDIwMTAsIGZyZXF1ZW5jeSA9IDUyKSAjIG1ha2UgYSB0cyBvYmplY3QNCg0KYGBgDQoNCiMjIyBMZXMg6XBpZOltaWVzIGRlIGdhc3Ryby1lbnTpcml0ZSBkZXB1aXMgMjAxMA0KDQpMZXMgZ3JhcGhpcXVlcyBzdWl2YW50cyBtb250cmVudCBsJ+l2b2x1dGlvbiBkZSBsJ2luZGljZSBkZXMgZ2FzdHJvLWVudOlyaXRlcyBlbiBGcmFuY2UgZGVwdWlzIDIwMTAuIFNhbnMgc3VycHJpc2UsIGxlcyBwaWNzIGQn6XBpZOltaWVzIHN1cnZpZW5uZW50IGVuIGfpbulyYWwgZGFucyBsZXMgcHJlbWnocmVzIGV0IGxlcyBkZXJuaehyZXMgc2VtYWluZXMgZGUgY2hhcXVlIGFubullLiANCg0KTGVzIGNvcnLpbG9ncmFtbWVzIG1ldHRlbnQgZW4g6XZpZGVuY2UgbGVzIGNvZWZmaWNpZW50cyBkJ2F1dG9jb3Jy6WxhdGlvbiBkZSBsYSBz6XJpZS4gTGEgcGx1cGFydCBzb250IHNpZ25pZmljYXRpdmVtZW50IGRpZmbpcmVudHMgZGUgMCwgY2UgcXVpIGTpbm90ZSBiaWVuIGRlIGxhIHNhaXNvbm5hbGl06SBkZSBsYSBz6XJpZS4gDQoNCmBgYHtyfQ0KIyB0aW1lIHNlcmllcyBzaW5jZSAyMDEwDQphdXRvcGxvdChHYXN0cm9fdHNfd2Vla3MpICsNCiAgICAgICAgbGFicyh0aXRsZSA9ICJFcGlk6W1pZXMgZGUgZ2FzdHJvLWVudOlyaXRlIGRlcHVpcyAyMDEwIiwgDQogICAgICAgICAgICAgeCA9ICJBbm7pZSIsIHkgPSAiSW5kaWNhdGV1ciBBdmFuY+kgU2FuaXRhaXJlIikNCg0KIyBzZWFzb25hbGl0eQ0KZ2dzZWFzb25wbG90KEdhc3Ryb190c193ZWVrcykgKw0KICAgICAgICBsYWJzKHRpdGxlID0gIkVwaXNvZGVzIGRlIGdhc3Ryby1lbnTpcml0ZSBwYXIgYW5u6WUiLCB4ID0gIlNlbWFpbmUiLCB5ID0gIklBUyIpDQpnZ3NlYXNvbnBsb3QoR2FzdHJvX3RzX3dlZWtzLCBwb2xhciA9IFRSVUUpICsNCiAgICAgICAgbGFicyh0aXRsZSA9ICJFcGlzb2RlcyBkZSBnYXN0cm8tZW506XJpdGUgcGFyIGFubullIiwgeCA9ICJTZW1haW5lIiwgeSA9ICJJQVMiKQ0KDQojIEFDRiBhbmQgUEFDRg0KZ2d0c2Rpc3BsYXkoR2FzdHJvX3RzX3dlZWtzKQ0KYGBgDQoNCg0KIyMjIE1vZOlsaXNhdGlvbnMgZXQgcHLpZGljdGlvbnMgZGVzIOlwaWTpbWllcyBkZSBnYXN0cm8tZW506XJpdGUgcG91ciBsZXMgYW5u6WVzIDIwMTktMjAyMA0KDQpMYSBwculkaWN0aW9uIHN1ciBsYSBiYXNlIGRlIHPpcmllcyBsb25naXR1ZGluYWxlcyDpdGFudCB0b3Vqb3VycyB1biBleGVyY2ljZSB0cuhzIGluY2VydGFpbiwgaWwgY29udmllbnQgZGUgcmVjb3VyaXIg4CBwbHVzaWV1cnMgdGVjaG5pcXVlcyBhZmluIGRlIGNvbXBhcmVyIGxldXJzIHF1YWxpdOlzIGV0IGTpZmF1dHMgcmVzcGVjdGlmcy4gQSBub3RlciBxdWUgZGFucyBsZXMgZ3JhcGhpcXVlcyBxdWkgc3VpdmVudCwgZGVzICoqaW50ZXJ2YWxsZXMgZGUgY29uZmlhbmNlIOAgODAgJSBldCA5NSAlKiogc29udCBhZmZpY2jpcyBlbiBjb3VsZXVyIGFmaW4gZGUgcmVuZHJlIGNvbXB0ZSBkdSBkZWdy6SBkJ2luY2VydGl0dWRlIGRlIGxhIHByb2plY3Rpb24uICANCg0KIyMjIyBQculkaWN0aW9ucyDgIHBhcnRpciBkZSBsYSBk6WNvbXBvc2l0aW9uIGRlIGxhIHNhaXNvbm5hbGl06Q0KDQpBIGwnYWlkZSBkJ3VuZSBk6WNvbXBvc2l0aW9uIGRlIHR5cGUgU1RMIChTZWFzb25hbCBhbmQgdHJlbmQgZGVjb21wb3NpdGlvbiB1c2luZyBsb2VzcyksIGlsIGVzdCBwb3NzaWJsZSBkZSBmYWlyZSBhcHBhcmHudHJlIGxhIHRlbmRhbmNlIGRlIGxvbmcgdGVybWUgZGUgbGEgc+lyaWUgYWluc2kgcXVlIHNhIHNhaXNvbm5hbGl06S4gQ29uZm9ybeltZW50IGF1eCBjb3Jy6WxvZ3JhbW1lcyB2dXMgcHLpY+lkZW1tZW50LCBsYSBz6XJpZSBjb21wb3J0ZSB1bmUgc2Fpc29ubmFsaXTpIHRy6HMgbWFycXXpZSAoZW4gZ3JpcykgbWFpcyBsYSB0ZW5kYW5jZSBsb3VyZGUgKGVuIHJvdWdlKSBlc3QgcmVsYXRpdmVtZW50IHN0YWJsZSwgYXUgbW9pbnMgZGVwdWlzIDIwMTUuIA0KDQpMZSBuaXZlYXUgcHLpZGl0IHBhciBsZSBtb2TobGUgcG91ciBsZXMgYW5u6WVzIDIwMTkgZXQgMjAyMCBwYXJhaXQgZmFpYmxlIGVuIGNlIHF1aSBjb25jZXJuZSBsZXMgc2VtYWluZXMgcXVpIG5lIGNvbm5haXNzZW50IHBhcyBkZSBmb3J0cyBwaWNzIGQn6XBpZOltaWVzLiANCg0KDQpgYGB7cn0NCkdhc3Ryb190c193ZWVrc1ssMV0gJT4lIG1zdGwocm9idXN0PVRSVUUpIC0+IGZpdF9tU1RMDQoNCmF1dG9wbG90KEdhc3Ryb190c193ZWVrcywgc2VyaWVzPSJEb25u6WVzIGJydXRlcyIpICsNCiAgYXV0b2xheWVyKHRyZW5kY3ljbGUoZml0X21TVEwpLCBzZXJpZXM9IlRlbmRhbmNlIikgKw0KICBhdXRvbGF5ZXIoc2Vhc2FkaihmaXRfbVNUTCksIHNlcmllcz0iQWp1c3RlbWVudCBkZSBsYSBzYWlzb25uYWxpdOkiKSArDQogIHhsYWIoIkFubullIikgKyB5bGFiKCJJQVMiKSArDQogIGdndGl0bGUoIkTpY29tcG9zaXRpb24gZGVzIOlwaWTpbWllcyBkZSBnYXN0cm8tZW506XJpdGUgZGVwdWlzIDIwMTAiKSArDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzPWMoImdyYXkiLCJibHVlIiwicmVkIiksDQogICAgICAgICAgICAgYnJlYWtzPWMoIkRvbm7pZXMgYnJ1dGVzIiwiQWp1c3RlbWVudCBkZSBsYSBzYWlzb25uYWxpdOkiLCJUZW5kYW5jZSIpKQ0KDQpmaXRfbVNUTCAlPiUgZm9yZWNhc3QobWV0aG9kPSJuYWl2ZSIsIGggPSAxMDQpICU+JQ0KICBhdXRvcGxvdCgpICsgeWxhYigiSUFTIikgKyB4bGFiKCJBbm7pZSIpICsgZ2d0aXRsZSgiUHLpZGljdGlvbnMgZGVzIOlwaWTpbWllcyBwb3VyIDIwMTktMjAyMCIpDQoNCmBgYA0KDQojIyMjIE1vZOhsZSBFVFMNCg0KTGUgbW9k6GxlIEVUUyAoRXJyb3IsIFRyZW5kLCBTZWFzb25hbCkgYXBwYXJ0aWVudCDgIGxhIGZhbWlsbGUgZHUgKipsaXNzYWdlIGV4cG9uZW50aWVsKiosIHF1aSAqKnBvbmTocmUgbGVzIG9ic2VydmF0aW9ucyBwYXNz6WVzKiogZGUgbWFuaehyZSBkZSBwbHVzIGVuIHBsdXMgZm9ydGUg4CBtZXN1cmUgcXVlIGxlcyBvYnNlcnZhdGlvbnMgc29udCBy6WNlbnRlcy4gDQoNCkVuIGwnb2NjdXJyZW5jZSwgbGUgbW9k6GxlIEVUUyBuJ2VzdCBwYXMgdW4gYm9uIGNob2l4IGRlIG1vZOlsaXNhdGlvbiwgY29tbWUgbGUgbW9udHJlIGwnZXJyZXVyIOlsZXbpZSAoQUlDYyA9IDQ0NjcpIGV0IGwnYW1wbGl0dWRlIGRlcyBpbnRlcnZhbGxlcyBkZSBjb25maWFuY2UuIExlIG5pdmVhdSBwculkaXQgcGFyIGxlIG1vZOhsZSBwb3VyIGxlcyBhbm7pZXMgMjAxOSBldCAyMDIwIHBhcmFpdCBkZSBub3V2ZWF1IGZhaWJsZSBlbiBjZSBxdWkgY29uY2VybmUgbGVzIHNlbWFpbmVzIHF1aSBuZSBjb25uYWlzc2VudCBwYXMgZGUgZm9ydHMgcGljcyBkJ+lwaWTpbWllcyA7IGNlIHLpc3VsdGF0IGVzdCBsb2dpcXVlIHB1aXNxdWUgbGUgbW9k6GxlIEVUUyBzJ2VzdCBhcHB1eekgc3VyIGxhIG3qbWUgbel0aG9kZSBkZSBk6WNvbXBvc2l0aW9uIFNUTC4gIA0KDQpgYGB7cn0NCmV0c19tb2RlbCA8LSBmb3JlY2FzdChHYXN0cm9fdHNfd2Vla3MsIGggPSAxMDQpDQpzdW1tYXJ5KGV0c19tb2RlbCkgIyBBSUNjID0gNDQ2Ny4zMzgNCmF1dG9wbG90KGV0c19tb2RlbCkgKw0KICAgICAgICBsYWJzKHRpdGxlID0gIlBy6WRpY3Rpb25zIGRlcyDpcGlk6W1pZXMgZGUgZ2FzdHJvLWVudOlyaXRlIHBvdXIgbGVzIGFubullcyAyMDE5LTIwMjAiLA0KICAgICAgICAgICAgIHggPSAiQW5u6WUiLCB5ID0gIklBUyIpDQoNCmBgYA0KDQojIyMjIE1vZOhsZSBBUklNQQ0KDQpBdSBjb250cmFpcmUgZGVzIG1vZOhsZXMgZGUgbGlzc2FnZSBleHBvbmVudGllbCwgdGVscyBxdSdFVFMsIGxlcyAqKm1vZOhsZXMgQVJJTUEqKiByZWNoZXJjaGVudCBsZXMgKipjb2VmZmljaWVudHMgZCdhdXRvY29yculsYXRpb24qKiBkZSBsYSBz6XJpZS4gDQoNCkVuIGwnb2NjdXJyZW5jZSwgcHVpc3F1ZSBsJ2luZGljZSBkZSBnYXN0cm8tZW506XJpdGUgY29ubmFpdCB1bmUgZm9ydGUgc2Fpc29ubmFsaXTpLCBpbCBlc3QgbuljZXNzYWlyZSBkZSAqKmRpZmbpcmVuY2llcioqIGF1IG1vaW5zIHVuZSBmb2lzIGxhIHPpcmllIHF1ZSBub3VzIOl0dWRpb25zIGFmaW4gZGUgbGEgcmVuZHJlIGRhdmFudGFnZSAqc3RhdGlvbm5haXJlKi4gTGVzIHBy6WRpY3Rpb25zIGlzc3VlcyBkdSBtb2TobGUgc2VtYmxlbnQgY3LpZGlibGVzIGNvbmNlcm5hbnQgbGVzIHBpY3MgZCfpcGlk6W1pZSwgbWFpcyBsZSBuaXZlYXUgZGUgbCdpbmRpY2UgcGFyYWl0IGZhaWJsZSBwb3VyIGxlcyBzZW1haW5lcyBob3JzIHBpY3MgZCfpcGlk6W1pZS4gDQoNCmBgYHtyfQ0KDQptb2RlbEFSSU1BXzEgPC0gYXV0by5hcmltYShHYXN0cm9fdHNfd2Vla3MsIGQgPSAxLCBhcHByb3hpbWF0aW9uPUZBTFNFLHRyYWNlPUZBTFNFKSAjIGQ9MSBtZWFucyB3ZSBkaWZmZXJlbnRpYXRlIG9uY2UNCnN1bW1hcnkobW9kZWxBUklNQV8xKSAjIEFSSU1BKDEsMSwxKSgwLDEsMilbNTJdIHdpdGggemVybyBtZWFuLiBBSWNjID0gMjc0Ny43Ng0KbW9kZWxBUklNQV8xICU+JSBmb3JlY2FzdChoID0gMTA0KSAlPiUgYXV0b3Bsb3QoKSArIA0KICAgICAgICBsYWJzKHRpdGxlID0gIlBy6WRpY3Rpb25zIGRlcyDpcGlk6W1pZXMgZGUgZ2FzdHJvLWVudOlyaXRlIHBvdXIgMjAxOS0yMDIwIiwNCiAgICAgICAgICAgICB4ID0gIkFubullIiwgeSA9ICJJbmRpY2UiKQ0KDQpgYGANCg0KIyMjIyBS6XNlYXUgZGUgbmV1cm9uZXMNCg0KTGVzIHLpc2VhdXggZGUgbmV1cm9uZXMgZGVtZXVyZW50IHJlbGF0aXZlbWVudCBwZXUgdXRpbGlz6XMgZGFucyBsZSBjYXMgZGUgc+lyaWVzIGxvbmdpdHVkaW5hbGVzLiBMZSBtb2TobGUgcmV0ZW51IGljaSBwb3Nz6GRlIGRldXggbmV1cm9uZXMgZGFucyBsJ3VuaXF1ZSBjb3VjaGUgY2FjaOllLiBTZXMgcHLpZGljdGlvbnMgcG91ciAyMDE5IGV0IDIwMjAgc2VtYmxlbnQgY3LpZGlibGVzIHBvdXIgbGVzIHNlbWFpbmVzIGhvcnMgcGljcyBkJ+lwaWTpbWllLCBlbiBjb21wYXJhaXNvbiBkZXMgcHJvamVjdGlvbnMgZGVzIHBy6WPpZGVudHMgbW9k6Gxlcy4gRW4gcmV2YW5jaGUsIGlsIHRlbmQgcHJvYmFibGVtZW50IOAgc291cy1lc3RpbWVyIGwnYW1wbGV1ciBkZXMgcGljcyBkJ+lwaWTpbWllLiANCg0KYGBge3J9DQojIGZpdCB0aGUgbW9kZWwNCmZpdE5OIDwtIG5uZXRhcihHYXN0cm9fdHNfd2Vla3MsIGxhbWJkYSA9IDApDQpzdW1tYXJ5KGZpdE5OKQ0KDQojIHBsb3QgdGhlIG1vZGVsJ3MgZm9yZWNhc3RzDQphdXRvcGxvdChmb3JlY2FzdChmaXROTiwgaCA9IDEwNCkpDQoNCiMgcHJlZGljdGlvbiBpbnRlcnZhbHMNCiMgY29tcHV0ZSBwcmVkaWN0aW9uIGludGVydmFscyB1c2luZyBzaW11bGF0aW9uIHdoZXJlIGZ1dHVyZSBzYW1wbGUgcGF0aHMgYXJlIGdlbmVyYXRlZCB1c2luZyBib290c3RyYXBwZWQgcmVzaWR1YWxzDQoNCnNpbSA8LSB0cyhtYXRyaXgoMCwgbnJvdz0yTCwgbmNvbD02TCksIHN0YXJ0ID0gZW5kKEdhc3Ryb190c193ZWVrcylbMUxdKzFMKQ0KDQpmb3IoaSBpbiBzZXEoNikpIHsNCiAgICAgICAgc2ltWyxpXSA8LSBzaW11bGF0ZShmaXROTiwgbnNpbT0yTCkNCiAgICAgICAgfQ0KDQogICAgICAgICMgcGxvdCB0aGUgcHJlZGljdGlvbiBJbnRlcnZhbHMgZm9yIHRoZSBmaXROTiBtb2RlbCANCmZjYXN0IDwtIGZvcmVjYXN0KGZpdE5OLCBQSSA9IFRSVUUsIGggPSAxMDQpDQphdXRvcGxvdChmY2FzdCkgKw0KICAgICAgICBsYWJzKHRpdGxlID0gIlBy6WRpY3Rpb25zIGRlcyDpcGlk6W1pZXMgZGUgZ2FzdHJvLWVudOlyaXRlIHBvdXIgMjAxOS0yMDIwIGF2ZWMgdW4gculzZWF1IGRlIG5ldXJvbmVzIiwgDQogICAgICAgICAgICAgeCA9ICJBbm7pZSIsIHkgPSAiSW5kaWNlIikNCmBgYA0KDQojIyMjIFRlcm1lcyBkZSBGb3VyaWVyDQoNCklsIGVzdCDpZ2FsZW1lbnQgcG9zc2libGUgZGUgcmVjb3VyaXIgYXV4ICoqdGVybWVzIGRlIEZvdXJpZXIqKiBjb21tZSB2YXJpYWJsZSBwculkaWN0aXZlIGRlIGxhIHNhaXNvbm5hbGl06SBkJ3VuIG1vZOhsZSBBUklNQS4gKipLKiogZOlzaWduZSBsZSAqKm5vbWJyZSBkZSBwYWlyZXMgZGUgc2ludXMgZXQgZGUgY29zaW51cyoqLiBJbCBlc3QgY2hvaXNpIOAgbCdpc3N1ZSBkJ3VuIGFyYml0cmFnZSBlbnRyZSBsYSBy6WR1Y3Rpb24gZGUgbCdlcnJldXIgKGljaSBtZXN1cullIHBhciBsJ0FJQ2MpIGV0IGxlIGRlZ3LpIGRlIGZsZXhpYmlsaXTpIHBvdXIg6XZpdGVyIGxlIHN1ci1hcHByZW50aXNzYWdlICgqb3ZlcmZpdHRpbmcqKS4gDQoNCkFwcuhzIGF2b2lyIGVzc2F56SA2IGRpZmbpcmVudGVzIHZhbGV1cnMgcG91ciBLLCBub3VzIHJldGVub25zICoqSz00KiosIHNvaXQgNCBwYWlyZXMgZGUgc2ludXMgZXQgZGUgY29zaW51cywgY2FyIGxlIGdhaW4gbWFyZ2luYWwgYXNzb2Np6SDgIGxhIDRlIHBhaXJlIGVzdCBmb3J0ZSBlbiB0ZXJtZXMgZGUgculkdWN0aW9uIGRlIGwnQUlDYyB0YW5kaXMgcXVlIGxhIDVlIHBhaXJlIG4nYXBwb3J0ZSBxdWUgcGV1IGRlIGLpbulmaWNlcyBhdSBwcml4IGQndW5lIGZsZXhpYmlsaXTpIHBvdGVudGllbGxlbWVudCB0cm9wIGZvcnRlLg0KDQpMZXMgcHLpZGljdGlvbnMgZGUgY2UgbW9k6GxlIHBvdXIgbGVzIGFubullcyAyMDE5IGV0IDIwMjAgc2VtYmxlbnQgdHJvcCBmYWlibGVzIGVuIGNlIHF1aSBjb25jZXJuZSBsZXMgc2VtYWluZXMgcXVpIG5lIGNvbm5haXNzZW50IHBhcyBkZSBmb3J0cyBwaWNzIGQn6XBpZOltaWUuIEVuIHJldmFuY2hlIGxlcyBzZW1haW5lcyBwb3VyIGxlc3F1ZWxsZXMgbGVzIOlwaWTpbWllcyBzb250IG5vbWJyZXVzZXMgcGFyYWlzc2VudCBtaWV1eCBhcHByb3hpbellcy4gDQoNCmBgYHtyfQ0KIyBGb3VyaWVyIDEvMg0KcGxvdHMgPC0gbGlzdCgpDQoNCmZvciAoaSBpbiBzZXEoNikpIHsNCiAgZml0IDwtIGF1dG8uYXJpbWEoR2FzdHJvX3RzX3dlZWtzLCB4cmVnID0gZm91cmllcihHYXN0cm9fdHNfd2Vla3MsIEsgPSBpKSwgc2Vhc29uYWwgPSBUUlVFLCBsYW1iZGEgPSAwKQ0KICBwbG90c1tbaV1dIDwtIGF1dG9wbG90KGZvcmVjYXN0KGZpdCwgeHJlZz1mb3VyaWVyKEdhc3Ryb190c193ZWVrcywgSz1pLCBoPTEwNCkpKSArDQogICAgeGxhYihwYXN0ZSgiSz0iLGksIkFJQ0M9Iixyb3VuZChmaXRbWyJhaWNjIl1dLDIpKSkgKw0KICAgIHlsYWIoIiIpDQp9DQoNCmdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlKCAjZnJvbSBwYWNrYWdlIGdyaWRFeHRyYQ0KICBwbG90c1tbMV1dLHBsb3RzW1syXV0scGxvdHNbWzNdXSwNCiAgcGxvdHNbWzRdXSxwbG90c1tbNV1dLHBsb3RzW1s2XV0sIG5yb3c9MykgDQoNCiMgS2VlcCBLID0gNCBtb2RlbCBiZWNhdXNlIGJleW9uZCB0aGUgcmVkdWN0aW9uIGluIEFJQ2MgaXMgd2Vhaw0KDQpgYGANCg0KYGBge3J9DQojIEZvdXJpZXIgMi8yDQpmb3VyaWVyX0dhc3RybyA8LSBhdXRvLmFyaW1hKEdhc3Ryb190c193ZWVrcywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhyZWcgPSBmb3VyaWVyKEdhc3Ryb190c193ZWVrcywgSyA9IDQpLCBzZWFzb25hbCA9IFRSVUUsIGxhbWJkYSA9IDApICMgSyA9IDQsIGNmIGdyYXBocw0KDQpzdW1tYXJ5KGZvdXJpZXJfR2FzdHJvKSAjIFJlZ3Jlc3Npb24gd2l0aCBBUklNQSgyLDEsMSkoMCwwLDEpWzUyXS4gQUlDYz0tMTYzMy42Mg0KDQphdXRvcGxvdChmb3JlY2FzdChmb3VyaWVyX0dhc3RybywgeHJlZz1mb3VyaWVyKEdhc3Ryb190c193ZWVrcywgSz00LCBoPTEwNCkpKSArDQogICAgICAgIGxhYnModGl0bGUgPSAiUHLpZGljdGlvbnMgZGVzIOlwaWTpbWllcyBkZSBnYXN0cm8tZW506XJpdGUgcG91ciAyMDE5LTIwMjAgYXZlYyBsZXMgdGVybWVzIGRlIEZvdXJpZXIiLCANCiAgICAgICAgICAgICB4ID0gIkFubullIiwgeSA9ICJJbmRpY2UiKQ0KYGBgDQoNCg0KIyMgQ29uY2x1c2lvbg0KDQpEaWZm6XJlbnRlcyBt6XRob2RlcyBvbnQg6XTpIHV0aWxpc+llcyBhZmluIGRlIHBy6WRpcmUgbGVzIOlwaWTpbWllcyBkZSBnYXN0cm8tZW506XJpdGUgZGVzIGFubullcyAyMDE5IGV0IDIwMjAuIExhIHBsdXBhcnQgZGVzIG1vZOhsZXMgdGVuZGVudCDgIHBy6WRpcmUgdW4gbml2ZWF1IGZhaWJsZSBkZSBsJ2luZGljZSBwb3VyIGxlcyBzZW1haW5lcyBob3JzIHBpY3MgZCfpcGlk6W1pZSwgY2UgcXVpIHBhcmFpdCBkb3V0ZXV4LiBQb3VyIGNlcyBzZW1haW5lcywgbGVzIHBy6WRpY3Rpb25zIGR1IHLpc2VhdSBkZSBuZXVyb25lcyBwYXJhaXNzZW50IGxlcyBwbHVzIGNy6WRpYmxlcy4gRW4gcmV2YW5jaGUsIGxlIHLpc2VhdSBkZSBuZXVyb25lcyBzZW1ibGUgc291cy1lc3RpbWVyIGwnYW1wbGV1ciBkZXMgcGljcyBkJ+lwaWTpbWllcywgbGVzcXVlbHMgcG91cnJhaWVudCDqdHJlIG1pZXV4IGFwcHJveGlt6XMgbm90YW1tZW50IHBhciBsZXMgbW9k6GxlcyBFVFMsIEFSSU1BIHZvaXJlIGF2ZWMgbGVzIHRlcm1lcyBkZSBGb3VyaWVyLiANCg0KRW5maW4sIGlsIGNvbnZpZW50IGRlIHJlbGV2ZXIgbGEgZ3JhbmRlIGluY2VydGl0dWRlIGF1dG91ciBkZSBjZXMgcHJvamVjdGlvbnMgYXUgcmVnYXJkIGRlIGwnKirpdGVuZHVlIGRlcyBpbnRlcnZhbGxlcyBkZSBjb25maWFuY2UqKiAoc3VydG91dCBwb3VyIDIwMjAsIGMnZXN0LeAtZGlyZSDgIHVuIGhvcml6b24gcGx1cyBsb2ludGFpbiksIGhvcm1pcyBwb3VyIGxlIHLpc2VhdSBkZSBuZXVyb25lcyBldCwgZGFucyB1bmUgY2VydGFpbmUgbWVzdXJlLCBsZXMgdGVybWVzIGRlIEZvdXJpZXIuIA0KDQpJbCBzZXJhaXQgb3Bwb3J0dW4gZGUgY2FsY3VsZXIsIOAgbGEgZmluIGRlIGwnYW5u6WUgMjAxOSwgbCdlcnJldXIgbW95ZW5uZSBjb21taXNlIHBhciBsZXMgZGlmZulyZW50cyBtb2TobGVzIGF1IHJlZ2FyZCBkZXMgdmFsZXVycyBvYnNlcnbpZXMsIGFmaW4gZCdvYnRlbmlyIGRlIG1laWxsZXVyZXMgcHLpZGljdGlvbnMgcG91ciAyMDIwLiANCg0K