Utiliser RStudio
Pour introduire le cours de séries temporelles j’utilise un notebook R Markdown. Je mettrai les codes en ligne, mais pour que vous puissiez l’utiliser il vous faudra installer R et RStudio (une interface graphique pour R). Ces logiciels sont disponibles sous Windows, macOS ou linux, pouvez (par exemple) suivre ces instructions pour l’installation. Par la suite, j’utiliserai cet environnement pour les applications du cours.
L’intérêt d’un notebook, est que nous pouvons mélanger dans un même fichier du texte, avec éventuellement des équations, du code et, si le code est éxécuté, des résultats (sous forme de graphiques, tableaux…). Pour écrire du code que l’on souhaite évaluer il suffit d’appuyer sur CTRL+ALT+i. RStudio insérera alors un bloc où on pourra écrire du code R. Pour éxécuter le code, il suffit de placer le curseur dans le bloc, et l’appuyer sur CTRL+ENTER. Le résultat du code s’affichera alors sous le bloc de code. Par exemple, pour obtenir une approximation de la racine de 2 avec R:
sqrt(2)
[1] 1.414214
Vous pouvez en fait dans cet environnement utiliser d’autres langages que R, par exemple du Python (ce qui peut être nécessaire dans certaines circonstances). L’output du bloc de code n’est pas forcément numérique, il peut s’agir d’un graphique ou d’un tableau. Par exemple :
plot(cars)

Enfin une fois terminé vous pouvez exporter le fichier, avec les résultats obtenus pour les blocs de code, dans un fichier .html
ou .pdf
. Pour générer la sortie html saisissez CTRL+SHIFT+k (cela devrait aussi afficher la page).
Dans notebook vous pouvez aussi écrire des expressions mathématiques en utilisant \(\LaTeX\). Pour placer une expression dans un paragraphe il suffit d’écrire l’expression \(\TeX\) entre des $ (comme vous le feriez dans un fichier .tex
). Par exemple $f(k) = {n \choose k} p^{k} (1-p)^{n-k}$
donnera \(f(k)={n \choose k}p^{k}(1-p)^{n-k}\) dans les fichiers .pdf
ou .html
générés. Comme dans un fichier .tex
, vous obtiendrez une équation centrée sur une ligne en plaçant l’expression \TeX
entre double $. Par exemple $$T(\hat\rho_T-1)\underset{T\rightarrow \infty}{\Longrightarrow}\frac{1}{2}\frac{W(1)^2-1}{\int_0^1W(r)^2\mathrm dr)}$$
, donnera :
\[T(\hat\rho_T-1)\underset{T\rightarrow \infty}{\Longrightarrow}\frac{1}{2}\frac{W(1)^2-1}{\int_0^1W(r)^2\mathrm dr)}\]
Vous trouverez une documentation complète sur ce qu’il est possible de faire dans un notebook R Markdown dans ce livre (en anglais). Pour ceux qui, tout comme moi, ne sont pas familier avec R
vous pouvez commencer par lire cette introduction (en français).
Télécharger et représenter des séries temporelles
Exemple 1 : la durée de vie des rois (et reines) d’Angleterre
Commençons par télécharger des données en utilisant la fonction scan
. Les données sont disponibles à l’adresse https://robjhyndman.com/tsdldata/misc/kings.dat, où le contenu du fichier kings.dat
est :
Age of Death of Successive Kings of England
#starting with William the Conqueror
#Source: McNeill, "Interactive Data Analysis"
60
43
67
50
56
42
50
65
68
43
65
34
47
34
49
41
13
35
53
56
16
43
69
59
48
59
86
55
68
51
33
49
67
77
81
67
71
81
68
70
77
56
Les trois premières lignes sont sans intérêt pour nous, nous les éliminons avec l’option skip
de la commande scan
(voir ici pour une documentation complète de cette fonction) :
kings <- scan("http://robjhyndman.com/tsdldata/misc/kings.dat",skip=3)
Read 42 items
kings
[1] 60 43 67 50 56 42 50 65 68 43 65 34 47 34 49 41 13 35 53 56 16 43 69 59 48 59 86 55
[29] 68 51 33 49 67 77 81 67 71 81 68 70 77 56
L’objet kings contient les données. Ensuite nous allons instancier un object ts
(pour time-series, ou série temporelle en français) à partir de ces données (une documentation complète est disponible ici) :
s1 <- ts(kings)
s1
Time Series:
Start = 1
End = 42
Frequency = 1
[1] 60 43 67 50 56 42 50 65 68 43 65 34 47 34 49 41 13 35 53 56 16 43 69 59 48 59 86 55
[29] 68 51 33 49 67 77 81 67 71 81 68 70 77 56
et nous pouvons représenter graphiquement ces données :
plot.ts(s1)

Cette série semble relativement régulière, on n’observe pas de tendance (dans le niveau ou la volatilité). Notons néanmoins qu’il s’agit d’un fort mauvais exemple (pour notre cours) car il ne s’agit pas véritablement d’une série temporelle, au sens où nous l’entendrons dans ce cours. Sur l’axe des abscisses nous avons le « numéro », dans l’ordre chronologique, des souverains qui ont régné depuis Guillaume de conquérant… Il s’agit d’une représentation assez discutable du temps. Les intervalles de temps entre les observations 1 et 2 et entre les observations 3 et 4 (je ne sais pas de qui il s’agit) ne sont a priori pas les mêmes.
Naissances à New-York
Données mensuelles sur le nombre de naissances à New York de janvier 1946 à décembre 1959.
births <- scan("http://robjhyndman.com/tsdldata/data/nybirths.dat")
Read 168 items
s2 <- ts(births, frequency=12, start=c(1946,1))
plot.ts(s2)

On distingue une composante tendancielle (une baisse au début, entre 14=946 et 1949, puis une augmentation régulière jusqu’à la fin de l’échantillon) et une composante saisonnière (certains mois il y a en moyenne moins de naissances). On verra plus loin que l’on peut décomposer les séries en trois composantes :
- composante tendancielle,
- composante saisonnière,
- résidus.
Dans ce cours on va surtout s’intéresser à la dernière composante.
Télécharger un fichier csv: compter les tâches solaires
On télécharge les données dans un fichier csv depuis le site du WDC-SILSO (Royal Observatory of Belgium, Brussels).
library (RCurl)
downloaddata <- getURL("www.sidc.be/silso/INFO/sndtotcsv.php")
ssp <- read.csv (text = downloaddata, sep = ';', header=FALSE)
ssp$V5[ssp$V5==-1] <- NA # Replace -1 by NA (missing values)
ssp
ssp = ts(ssp$V5, frequency=365, start=c(1818, 1, 1))
plot.ts(ssp)

On observe un comportement « saisonnier » (plutôt cyclique) mais la série temporelle semble stable (pas de tendance, c’est probablement heureux).
Télécharger des données économiques avec dbnomics
Voir le site https://db.nomics.world/
On récupère une série sur le PIB français en fréquence trimestrielle (volume base 100 en 2005) où les données sont corrigées de la saisonnalité.
library(rdbnomics) # Voir https://git.nomics.world/dbnomics/rdbnomics
Registered S3 method overwritten by 'data.table':
method from
print.data.table
Visit <https://db.nomics.world>.
dbnomicsdata <- rdb(ids = c("Eurostat/namq_10_gdp/Q.CLV_I05.SCA.B1GQ.FR")) # On récupère l'adresse sur db.nomics.world
dbnomicsdata
gdp <- ts(dbnomicsdata$value, frequency=4, start = c(1975,1))
#lgdp <- log(gdp) # Si on veut représenter le log
#ggdp <- 400*diff(lgdp) # Approximation du taux de croissance
plot.ts(gdp)

Ici on observe clairement des fluctuations autour d’une tendance (pas de saisonnalité puisque les données téléchargées sont déjà corrigées pour cette composante).
Identifier les composantes d’une série temporelle
Nous avons vu que série sur les naissances à New-York admet trois composantes : une tendance, un cycle (saisonnier) et un terme résiduel (la différence entre la série observée et les deux premières composantes). Quand on cherche à prédire une série temporelle il peut être intéressant d’identifier ces composantes. Il est relativement simple de faire des prédictions sur les deux premières composantes.
s2components <- decompose(s2)
plot(s2components)

Dans ce cours on s’intéressera essentiellement à la dernière composante, en proposant des modèles qui permettent de construire des prévisions. Si on s’intéresse simultanément à la tendance et au terme résiduel; il suffit de rettrancher la composante saisonnière à la série observée :
s2seasonallyadjusted <- s2 - s2components$seasonal
plot.ts(s2seasonallyadjusted)

LS0tCnRpdGxlOiAiU8OpcmllcyB0ZW1wb3JlbGxlcyAodW5lIGludHJvZHVjdGlvbiBhdmVjIFIpIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogbnVsbAogIGh0bWxfZG9jdW1lbnQ6CiAgICBkZl9wcmludDogcGFnZWQKLS0tCgojIFV0aWxpc2VyIFJTdHVkaW8KClBvdXIgaW50cm9kdWlyZSBsZSBjb3VycyBkZSBzw6lyaWVzIHRlbXBvcmVsbGVzIGondXRpbGlzZSB1biBub3RlYm9vayBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkuIEplIG1ldHRyYWkgbGVzIGNvZGVzIGVuIGxpZ25lLCBtYWlzIHBvdXIgcXVlIHZvdXMgcHVpc3NpZXogbCd1dGlsaXNlciBpbCB2b3VzIGZhdWRyYSBpbnN0YWxsZXIgUiBldCBSU3R1ZGlvICh1bmUgaW50ZXJmYWNlIGdyYXBoaXF1ZSBwb3VyIFIpLiBDZXMgbG9naWNpZWxzIHNvbnQgZGlzcG9uaWJsZXMgc291cyBXaW5kb3dzLCBtYWNPUyBvdSBsaW51eCwgcG91dmV6IChwYXIgZXhlbXBsZSkgc3VpdnJlIGNlcyBbaW5zdHJ1Y3Rpb25zXShodHRwczovL2xhcm1hcmFuZ2UuZ2l0aHViLmlvL2FuYWx5c2UtUi9pbnN0YWxsYXRpb24tZGUtUi1ldC1SU3R1ZGlvLmh0bWwpIHBvdXIgbCdpbnN0YWxsYXRpb24uIFBhciBsYSBzdWl0ZSwgaid1dGlsaXNlcmFpIGNldCBlbnZpcm9ubmVtZW50IHBvdXIgbGVzIGFwcGxpY2F0aW9ucyBkdSBjb3Vycy4KCkwnaW50w6lyw6p0IGQndW4gbm90ZWJvb2ssIGVzdCBxdWUgbm91cyBwb3V2b25zIG3DqWxhbmdlciBkYW5zIHVuIG3Dqm1lIGZpY2hpZXIgZHUgdGV4dGUsIGF2ZWMgw6l2ZW50dWVsbGVtZW50IGRlcyDDqXF1YXRpb25zLCBkdSBjb2RlIGV0LCBzaSBsZSBjb2RlIGVzdCDDqXjDqWN1dMOpLCBkZXMgcsOpc3VsdGF0cyAoc291cyBmb3JtZSBkZSBncmFwaGlxdWVzLCB0YWJsZWF1eC4uLikuIFBvdXIgw6ljcmlyZSBkdSBjb2RlIHF1ZSBsJ29uIHNvdWhhaXRlIMOpdmFsdWVyIGlsIHN1ZmZpdCBkJ2FwcHV5ZXIgc3VyIENUUkwrQUxUK2kuIFJTdHVkaW8gaW5zw6lyZXJhIGFsb3JzIHVuIGJsb2Mgb8O5IG9uIHBvdXJyYSDDqWNyaXJlIGR1IGNvZGUgUi4gUG91ciDDqXjDqWN1dGVyIGxlIGNvZGUsIGlsIHN1ZmZpdCBkZSBwbGFjZXIgbGUgY3Vyc2V1ciBkYW5zIGxlIGJsb2MsIGV0IGwnYXBwdXllciBzdXIgQ1RSTCtFTlRFUi4gTGUgcsOpc3VsdGF0IGR1IGNvZGUgcydhZmZpY2hlcmEgYWxvcnMgc291cyBsZSBibG9jIGRlIGNvZGUuIFBhciBleGVtcGxlLCBwb3VyIG9idGVuaXIgdW5lIGFwcHJveGltYXRpb24gZGUgbGEgcmFjaW5lIGRlIDIgYXZlYyBSOiAKCmBgYHtyfQpzcXJ0KDIpCmBgYAoKVm91cyBwb3V2ZXogZW4gZmFpdCBkYW5zIGNldCBlbnZpcm9ubmVtZW50IHV0aWxpc2VyIGQnYXV0cmVzIGxhbmdhZ2VzIHF1ZSBSLCBwYXIgZXhlbXBsZSBkdSBQeXRob24gKGNlIHF1aSBwZXV0IMOqdHJlIG7DqWNlc3NhaXJlIGRhbnMgY2VydGFpbmVzIGNpcmNvbnN0YW5jZXMpLiBMJ291dHB1dCBkdSBibG9jIGRlIGNvZGUgbidlc3QgcGFzIGZvcmPDqW1lbnQgbnVtw6lyaXF1ZSwgaWwgcGV1dCBzJ2FnaXIgZCd1biBncmFwaGlxdWUgb3UgZCd1biB0YWJsZWF1LiBQYXIgZXhlbXBsZSA6CgpgYGB7cn0KcGxvdChjYXJzKQpgYGAKCkVuZmluIHVuZSBmb2lzIHRlcm1pbsOpIHZvdXMgcG91dmV6IGV4cG9ydGVyIGxlIGZpY2hpZXIsIGF2ZWMgbGVzIHLDqXN1bHRhdHMgb2J0ZW51cyBwb3VyIGxlcyBibG9jcyBkZSBjb2RlLCBkYW5zIHVuIGZpY2hpZXIgYC5odG1sYCBvdSBgLnBkZmAuIFBvdXIgZ8OpbsOpcmVyIGxhIHNvcnRpZSBodG1sIHNhaXNpc3NleiBDVFJMK1NISUZUK2sgKGNlbGEgZGV2cmFpdCBhdXNzaSBhZmZpY2hlciBsYSBwYWdlKS4KCkRhbnMgbm90ZWJvb2sgdm91cyBwb3V2ZXogYXVzc2kgw6ljcmlyZSBkZXMgZXhwcmVzc2lvbnMgbWF0aMOpbWF0aXF1ZXMgZW4gdXRpbGlzYW50ICRcTGFUZVgkLiBQb3VyIHBsYWNlciB1bmUgZXhwcmVzc2lvbiBkYW5zIHVuIHBhcmFncmFwaGUgaWwgc3VmZml0IGQnw6ljcmlyZSBsJ2V4cHJlc3Npb24gJFxUZVgkIGVudHJlIGRlcyBcJCAoY29tbWUgdm91cyBsZSBmZXJpZXogZGFucyB1biBmaWNoaWVyIGAudGV4YCkuIFBhciBleGVtcGxlIGAkZihrKSA9IHtuIFxjaG9vc2Uga30gcF57a30gKDEtcClee24ta30kYCBkb25uZXJhICRmKGspPXtuIFxjaG9vc2Uga31wXntrfSgxLXApXntuLWt9JCBkYW5zIGxlcyBmaWNoaWVycyBgLnBkZmAgb3UgYC5odG1sYCBnw6luw6lyw6lzLiBDb21tZSBkYW5zIHVuIGZpY2hpZXIgYC50ZXhgLCB2b3VzIG9idGllbmRyZXogdW5lIMOpcXVhdGlvbiBjZW50csOpZSBzdXIgdW5lIGxpZ25lIGVuIHBsYcOnYW50IGwnZXhwcmVzc2lvbiBgXFRlWGAgZW50cmUgZG91YmxlIFwkLiBQYXIgZXhlbXBsZSBgJCRUKFxoYXRccmhvX1QtMSlcdW5kZXJzZXR7VFxyaWdodGFycm93IFxpbmZ0eX17XExvbmdyaWdodGFycm93fVxmcmFjezF9ezJ9XGZyYWN7VygxKV4yLTF9e1xpbnRfMF4xVyhyKV4yXG1hdGhybSBkcil9JCRgLCBkb25uZXJhIDoKCiQkVChcaGF0XHJob19ULTEpXHVuZGVyc2V0e1RccmlnaHRhcnJvdyBcaW5mdHl9e1xMb25ncmlnaHRhcnJvd31cZnJhY3sxfXsyfVxmcmFje1coMSleMi0xfXtcaW50XzBeMVcocileMlxtYXRocm0gZHIpfSQkCgpWb3VzIHRyb3V2ZXJleiB1bmUgZG9jdW1lbnRhdGlvbiBjb21wbMOodGUgc3VyIGNlIHF1J2lsIGVzdCBwb3NzaWJsZSBkZSBmYWlyZSBkYW5zIHVuIG5vdGVib29rIFtSIE1hcmtkb3duXShodHRwOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tKSBkYW5zIGNlIFtsaXZyZV0oaHR0cHM6Ly9ib29rZG93bi5vcmcveWlodWkvcm1hcmtkb3duLykgKGVuIGFuZ2xhaXMpLiBQb3VyIGNldXggcXVpLCB0b3V0IGNvbW1lIG1vaSwgbmUgc29udCBwYXMgZmFtaWxpZXIgYXZlYyBgUmAgdm91cyBwb3V2ZXogY29tbWVuY2VyIHBhciBsaXJlIGNldHRlIFtpbnRyb2R1Y3Rpb25dKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL2RvYy9jb250cmliL1BhcmFkaXMtcmRlYnV0c19mci5wZGYpIChlbiBmcmFuw6dhaXMpLgoKIyBUw6lsw6ljaGFyZ2VyIGV0IHJlcHLDqXNlbnRlciBkZXMgc8OpcmllcyB0ZW1wb3JlbGxlcwoKIyMgRXhlbXBsZSAxIDogbGEgZHVyw6llIGRlIHZpZSBkZXMgcm9pcyAoZXQgcmVpbmVzKSBkJ0FuZ2xldGVycmUKCkNvbW1lbsOnb25zIHBhciB0w6lsw6ljaGFyZ2VyIGRlcyBkb25uw6llcyBlbiB1dGlsaXNhbnQgbGEgZm9uY3Rpb24gIGBzY2FuYC4gTGVzIGRvbm7DqWVzIHNvbnQgZGlzcG9uaWJsZXMgw6AgbCdhZHJlc3NlIGh0dHBzOi8vcm9iamh5bmRtYW4uY29tL3RzZGxkYXRhL21pc2Mva2luZ3MuZGF0LCBvw7kgbGUgY29udGVudSBkdSBmaWNoaWVyIGBraW5ncy5kYXRgIGVzdMKgOgoKYGBgCkFnZSBvZiBEZWF0aCBvZiBTdWNjZXNzaXZlIEtpbmdzIG9mIEVuZ2xhbmQKI3N0YXJ0aW5nIHdpdGggV2lsbGlhbSB0aGUgQ29ucXVlcm9yCiNTb3VyY2U6IE1jTmVpbGwsICJJbnRlcmFjdGl2ZSBEYXRhIEFuYWx5c2lzIgo2MAo0Mwo2Nwo1MAo1Ngo0Mgo1MAo2NQo2OAo0Mwo2NQozNAo0NwozNAo0OQo0MQoxMwozNQo1Mwo1NgoxNgo0Mwo2OQo1OQo0OAo1OQo4Ngo1NQo2OAo1MQozMwo0OQo2Nwo3Nwo4MQo2Nwo3MQo4MQo2OAo3MAo3Nwo1NgpgYGAKCkxlcyB0cm9pcyBwcmVtacOocmVzIGxpZ25lcyBzb250IHNhbnMgaW50w6lyw6p0IHBvdXIgbm91cywgbm91cyBsZXMgw6lsaW1pbm9ucyBhdmVjIGwnb3B0aW9uIGBza2lwYCBkZSBsYSBjb21tYW5kZSAgYHNjYW5gICh2b2lyIFtpY2ldKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9iYXNlL3ZlcnNpb25zLzMuNi4yL3RvcGljcy9zY2FuKSBwb3VyIHVuZSBkb2N1bWVudGF0aW9uIGNvbXBsw6h0ZSBkZSBjZXR0ZSBmb25jdGlvbinCoDoKCmBgYHtyfQpraW5ncyA8LSBzY2FuKCJodHRwOi8vcm9iamh5bmRtYW4uY29tL3RzZGxkYXRhL21pc2Mva2luZ3MuZGF0Iixza2lwPTMpCmtpbmdzCmBgYApMJ29iamV0IGtpbmdzIGNvbnRpZW50IGxlcyBkb25uw6llcy4gRW5zdWl0ZSBub3VzIGFsbG9ucyBpbnN0YW5jaWVyIHVuIG9iamVjdCBgdHNgIChwb3VyIHRpbWUtc2VyaWVzLCBvdSBzw6lyaWUgdGVtcG9yZWxsZSBlbiBmcmFuw6dhaXMpIMOgIHBhcnRpciBkZSBjZXMgZG9ubsOpZXMgKHVuZSBkb2N1bWVudGF0aW9uIGNvbXBsw6h0ZSBlc3QgZGlzcG9uaWJsZSBbaWNpXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvc3RhdHMvdmVyc2lvbnMvMy42LjIvdG9waWNzL3RzKSnCoDoKCmBgYHtyfQpzMSA8LSB0cyhraW5ncykKczEKYGBgCmV0IG5vdXMgcG91dm9ucyByZXByw6lzZW50ZXIgZ3JhcGhpcXVlbWVudCBjZXMgZG9ubsOpZXPCoDoKCmBgYHtyfQpwbG90LnRzKHMxKQpgYGAKCkNldHRlIHPDqXJpZSBzZW1ibGUgcmVsYXRpdmVtZW50IHLDqWd1bGnDqHJlLCBvbiBuJ29ic2VydmUgcGFzIGRlIHRlbmRhbmNlIChkYW5zIGxlIG5pdmVhdSBvdSBsYSB2b2xhdGlsaXTDqSkuIE5vdG9ucyBuw6lhbm1vaW5zIHF1J2lsIHMnYWdpdCBkJ3VuIGZvcnQgbWF1dmFpcyBleGVtcGxlIChwb3VyIG5vdHJlIGNvdXJzKSBjYXIgaWwgbmUgcydhZ2l0IHBhcyB2w6lyaXRhYmxlbWVudCBkJ3VuZSBzw6lyaWUgdGVtcG9yZWxsZSwgYXUgc2VucyBvw7kgbm91cyBsJ2VudGVuZHJvbnMgZGFucyBjZSBjb3Vycy4gU3VyIGwnYXhlIGRlcyBhYnNjaXNzZXMgbm91cyBhdm9ucyBsZSDCqyBudW3DqXJvIMK7LCBkYW5zIGwnb3JkcmUgY2hyb25vbG9naXF1ZSwgZGVzIHNvdXZlcmFpbnMgcXVpIG9udCByw6lnbsOpIGRlcHVpcyBHdWlsbGF1bWUgZGUgY29ucXXDqXJhbnQuLi4gSWwgcydhZ2l0IGQndW5lIHJlcHLDqXNlbnRhdGlvbiBhc3NleiBkaXNjdXRhYmxlIGR1IHRlbXBzLiBMZXMgaW50ZXJ2YWxsZXMgZGUgdGVtcHMgZW50cmUgbGVzIG9ic2VydmF0aW9ucyAxIGV0IDIgZXQgZW50cmUgbGVzIG9ic2VydmF0aW9ucyAzIGV0IDQgKGplIG5lIHNhaXMgcGFzIGRlIHF1aSBpbCBzJ2FnaXQpIG5lIHNvbnQgYSBwcmlvcmkgcGFzIGxlcyBtw6ptZXMuCgojIyBOYWlzc2FuY2VzIMOgIE5ldy1Zb3JrCgpEb25uw6llcyBtZW5zdWVsbGVzIHN1ciBsZSBub21icmUgZGUgbmFpc3NhbmNlcyDDoCBOZXcgWW9yayBkZSBqYW52aWVyIDE5NDYgw6AgZMOpY2VtYnJlIDE5NTkuCgpgYGB7cn0KYmlydGhzIDwtIHNjYW4oImh0dHA6Ly9yb2JqaHluZG1hbi5jb20vdHNkbGRhdGEvZGF0YS9ueWJpcnRocy5kYXQiKQpzMiA8LSB0cyhiaXJ0aHMsIGZyZXF1ZW5jeT0xMiwgc3RhcnQ9YygxOTQ2LDEpKQpwbG90LnRzKHMyKQpgYGAKCk9uIGRpc3Rpbmd1ZSB1bmUgY29tcG9zYW50ZSB0ZW5kYW5jaWVsbGUgKHVuZSBiYWlzc2UgYXUgZMOpYnV0LCBlbnRyZSAxND05NDYgZXQgMTk0OSwgcHVpcyB1bmUgYXVnbWVudGF0aW9uIHLDqWd1bGnDqHJlIGp1c3F1J8OgIGxhIGZpbiBkZSBsJ8OpY2hhbnRpbGxvbikgZXQgdW5lIGNvbXBvc2FudGUgc2Fpc29ubmnDqHJlIChjZXJ0YWlucyBtb2lzIGlsIHkgYSBlbiBtb3llbm5lIG1vaW5zIGRlIG5haXNzYW5jZXMpLiBPbiB2ZXJyYSBwbHVzIGxvaW4gcXVlIGwnb24gcGV1dCBkw6ljb21wb3NlciBsZXMgc8OpcmllcyBlbiB0cm9pcyBjb21wb3NhbnRlcyA6IAoKIC0gY29tcG9zYW50ZSB0ZW5kYW5jaWVsbGUsCiAtIGNvbXBvc2FudGUgc2Fpc29ubmnDqHJlLAogLSByw6lzaWR1cy4KIApEYW5zIGNlIGNvdXJzIG9uIHZhIHN1cnRvdXQgcydpbnTDqXJlc3NlciDDoCBsYSBkZXJuacOocmUgY29tcG9zYW50ZS4gCgojIyBUw6lsw6ljaGFyZ2VyIHVuIGZpY2hpZXIgY3N2OiBjb21wdGVyIGxlcyB0w6JjaGVzIHNvbGFpcmVzCgpPbiB0w6lsw6ljaGFyZ2UgbGVzIGRvbm7DqWVzIGRhbnMgdW4gZmljaGllciBjc3YgZGVwdWlzIGxlIHNpdGUgZHUgV0RDLVNJTFNPIChSb3lhbCBPYnNlcnZhdG9yeSBvZiBCZWxnaXVtLCBCcnVzc2VscykuCgpgYGB7cn0KbGlicmFyeSAoUkN1cmwpCmRvd25sb2FkZGF0YSA8LSBnZXRVUkwoInd3dy5zaWRjLmJlL3NpbHNvL0lORk8vc25kdG90Y3N2LnBocCIpCnNzcCA8LSByZWFkLmNzdiAodGV4dCA9IGRvd25sb2FkZGF0YSwgc2VwID0gJzsnLCBoZWFkZXI9RkFMU0UpCnNzcCRWNVtzc3AkVjU9PS0xXSA8LSBOQSAjIFJlcGxhY2UgLTEgYnkgTkEgKG1pc3NpbmcgdmFsdWVzKQpzc3AKYGBgCgpgYGB7cn0Kc3NwID0gdHMoc3NwJFY1LCBmcmVxdWVuY3k9MzY1LCBzdGFydD1jKDE4MTgsIDEsIDEpKQpwbG90LnRzKHNzcCkKYGBgCgpPbiBvYnNlcnZlIHVuIGNvbXBvcnRlbWVudCDCqyBzYWlzb25uaWVyIMK7IChwbHV0w7R0IGN5Y2xpcXVlKSBtYWlzIGxhIHPDqXJpZSB0ZW1wb3JlbGxlIHNlbWJsZSBzdGFibGUgKHBhcyBkZSB0ZW5kYW5jZSwgYydlc3QgcHJvYmFibGVtZW50IGhldXJldXgpLgoKIyMgVMOpbMOpY2hhcmdlciBkZXMgZG9ubsOpZXMgw6ljb25vbWlxdWVzIGF2ZWMgZGJub21pY3MKClZvaXIgbGUgc2l0ZSBodHRwczovL2RiLm5vbWljcy53b3JsZC8KCk9uIHLDqWN1cMOocmUgdW5lIHPDqXJpZSBzdXIgbGUgUElCIGZyYW7Dp2FpcyBlbiBmcsOpcXVlbmNlIHRyaW1lc3RyaWVsbGUgKHZvbHVtZSBiYXNlIDEwMCBlbiAyMDA1KSBvw7kgbGVzIGRvbm7DqWVzIHNvbnQgY29ycmlnw6llcyBkZSBsYSBzYWlzb25uYWxpdMOpLgoKYGBge3J9CmxpYnJhcnkocmRibm9taWNzKSAgIyBWb2lyIGh0dHBzOi8vZ2l0Lm5vbWljcy53b3JsZC9kYm5vbWljcy9yZGJub21pY3MKZGJub21pY3NkYXRhIDwtIHJkYihpZHMgPSBjKCJFdXJvc3RhdC9uYW1xXzEwX2dkcC9RLkNMVl9JMDUuU0NBLkIxR1EuRlIiKSkgIyBPbiByw6ljdXDDqHJlIGwnYWRyZXNzZSBzdXIgZGIubm9taWNzLndvcmxkCmRibm9taWNzZGF0YQpgYGAKCmBgYHtyfQpnZHAgPC0gdHMoZGJub21pY3NkYXRhJHZhbHVlLCBmcmVxdWVuY3k9NCwgc3RhcnQgPSBjKDE5NzUsMSkpCiNsZ2RwIDwtIGxvZyhnZHApICAgIyBTaSBvbiB2ZXV0IHJlcHLDqXNlbnRlciBsZSBsb2cKI2dnZHAgPC0gNDAwKmRpZmYobGdkcCkgIyBBcHByb3hpbWF0aW9uIGR1IHRhdXggZGUgY3JvaXNzYW5jZQpwbG90LnRzKGdkcCkKYGBgCgpJY2kgb24gb2JzZXJ2ZSBjbGFpcmVtZW50IGRlcyBmbHVjdHVhdGlvbnMgYXV0b3VyIGQndW5lIHRlbmRhbmNlIChwYXMgZGUgc2Fpc29ubmFsaXTDqSBwdWlzcXVlIGxlcyBkb25uw6llcyB0w6lsw6ljaGFyZ8OpZXMgc29udCBkw6lqw6AgY29ycmlnw6llcyBwb3VyIGNldHRlIGNvbXBvc2FudGUpLgoKIyBJZGVudGlmaWVyIGxlcyBjb21wb3NhbnRlcyBkJ3VuZSBzw6lyaWUgdGVtcG9yZWxsZQoKTm91cyBhdm9ucyB2dSBxdWUgc8OpcmllIHN1ciBsZXMgbmFpc3NhbmNlcyDDoCBOZXctWW9yayBhZG1ldCB0cm9pcyBjb21wb3NhbnRlcyA6IHVuZSB0ZW5kYW5jZSwgdW4gY3ljbGUgKHNhaXNvbm5pZXIpIGV0IHVuIHRlcm1lIHLDqXNpZHVlbCAobGEgZGlmZsOpcmVuY2UgZW50cmUgbGEgc8OpcmllIG9ic2VydsOpZSBldCBsZXMgZGV1eCBwcmVtacOocmVzIGNvbXBvc2FudGVzKS4gUXVhbmQgb24gY2hlcmNoZSDDoCBwcsOpZGlyZSB1bmUgc8OpcmllIHRlbXBvcmVsbGUgaWwgcGV1dCDDqnRyZSBpbnTDqXJlc3NhbnQgZCdpZGVudGlmaWVyIGNlcyBjb21wb3NhbnRlcy4gSWwgZXN0IHJlbGF0aXZlbWVudCBzaW1wbGUgZGUgZmFpcmUgZGVzIHByw6lkaWN0aW9ucyBzdXIgbGVzIGRldXggcHJlbWnDqHJlcyBjb21wb3NhbnRlcy4KCmBgYHtyfQpzMmNvbXBvbmVudHMgPC0gZGVjb21wb3NlKHMyKQpwbG90KHMyY29tcG9uZW50cykKYGBgCgpEYW5zIGNlIGNvdXJzIG9uIHMnaW50w6lyZXNzZXJhIGVzc2VudGllbGxlbWVudCDDoCBsYSBkZXJuacOocmUgY29tcG9zYW50ZSwgZW4gcHJvcG9zYW50IGRlcyBtb2TDqGxlcyBxdWkgcGVybWV0dGVudCBkZSBjb25zdHJ1aXJlIGRlcyBwcsOpdmlzaW9ucy4gU2kgb24gcydpbnTDqXJlc3NlIHNpbXVsdGFuw6ltZW50IMOgIGxhIHRlbmRhbmNlIGV0IGF1IHRlcm1lIHLDqXNpZHVlbDsgaWwgc3VmZml0IGRlIHJldHRyYW5jaGVyIGxhIGNvbXBvc2FudGUgc2Fpc29ubmnDqHJlIMOgIGxhIHPDqXJpZSBvYnNlcnbDqWUgOgoKYGBge3J9CnMyc2Vhc29uYWxseWFkanVzdGVkIDwtIHMyIC0gczJjb21wb25lbnRzJHNlYXNvbmFsCnBsb3QudHMoczJzZWFzb25hbGx5YWRqdXN0ZWQpCmBgYAoK