Motivação

Case 1:

Você começou a trabalhar no ministério de turismo e relações exteriores da Austrália e sua primeira tarefa é fazer a previsão do número de viajantes que chegarão ao País no próximo trimestre (mas também querem saber a previsão por estado e região, se for possível).

Australia dividida por estados

O conjunto de dados a ser análisado já está disponível na empresa e é carregado no R com o nome tourism

library(fpp3)
library(patchwork)
glimpse(tourism)
Rows: 24,320
Columns: 5
Key: Region, State, Purpose [304]
$ Quarter <qtr> 1998 Q1, 1998 Q2, 1998 Q3, 1998 Q4, 19…
$ Region  <chr> "Adelaide", "Adelaide", "Adelaide", "A…
$ State   <chr> "South Australia", "South Australia", …
$ Purpose <chr> "Business", "Business", "Business", "B…
$ Trips   <dbl> 135.0777, 109.9873, 166.0347, 127.1605…

O ministério não considera importante separar o motivo da viagen. Assim você precisa agregar os dados por região e estado.

tourism_hts <- tourism |>
  aggregate_key(State / Region, Trips = sum(Trips))
tourism_hts

Observação: a função aggregate_key() tem uma nomenclatura de pai/filho.

tourism_hts |>
  filter(is_aggregated(Region)) |>
  autoplot(Trips) +
  labs(y = "Trips ('000)",
       title = "Chegadas na Austrália: nacional e por estados.") +
  facet_wrap(vars(State), scales = "free_y", ncol = 3) +
  theme(legend.position = "none")

Por curiosidade, você resolve ver qual é o comportamento das regiões dentro de cada estado.

tourism_hts |>
  filter(!is_aggregated(Region)) |>
  autoplot(Trips) +
  labs(y = "Trips ('000)",
       title = "Chegadas na Austrália: nacional e por estados.") +
  facet_wrap(vars(State), scales = "free_y", ncol = 3) +
  theme(legend.position = "none")

Este tipo de dados de séries temporais são conhecidos como séries temporis hierárquicas, pois possuem uma hierarquia.

Se \(y_t\) denotar o número de chegadas na Austrália no tempo \(t\), e \(y_{j,t}\) denotar o número de chegadas para o estados/região da Austrália no tempo \(t\). Teriamos que

\[y_t = \underbrace{y_{AA, t} + y_{AB, t} + y_{AC, t}}_{y_{A, t}} + \underbrace{y_{BA, t} + y_{BB, t}}_{y_{B, t}}\]

Case 2:

Você começa a trabalhar no judiciário da Austrália e sua primeira tarefa é fazer uma previsão do total (mas também, se for possível, por sexo, situação legal, estado) de presos para o próximo trimestre. Os dados estão disponível no seguinte link

prison <- readr::read_csv("https://OTexts.com/fpp3/extrafiles/prison_population.csv")
glimpse(prison)
Rows: 3,072
Columns: 6
$ Date       <date> 2005-03-01, 2005-03-01, 2005-03-01…
$ State      <chr> "ACT", "ACT", "ACT", "ACT", "ACT", …
$ Gender     <chr> "Female", "Female", "Female", "Fema…
$ Legal      <chr> "Remanded", "Remanded", "Sentenced"…
$ Indigenous <chr> "ATSI", "Non-ATSI", "ATSI", "Non-AT…
$ Count      <dbl> 0, 2, 0, 5, 7, 58, 5, 101, 51, 131,…
prison$Date[c(1, 2, 85, 86, 469, 470)]
[1] "2005-03-01" "2005-03-01" "2005-06-01" "2005-06-01"
[5] "2006-12-01" "2006-12-01"
prison <- prison |> 
  mutate(Quarter = yearquarter(Date)) |>
  select(-Date)  |>
  as_tsibble(key = c(Gender, Legal, State, Indigenous),
             index = Quarter) |>
  relocate(Quarter)
glimpse(prison)
Rows: 3,072
Columns: 6
Key: State, Gender, Legal, Indigenous [64]
$ Quarter    <qtr> 2005 Q1, 2005 Q2, 2005 Q3, 2005 Q4,…
$ State      <chr> "ACT", "ACT", "ACT", "ACT", "ACT", …
$ Gender     <chr> "Female", "Female", "Female", "Fema…
$ Legal      <chr> "Remanded", "Remanded", "Remanded",…
$ Indigenous <chr> "ATSI", "ATSI", "ATSI", "ATSI", "AT…
$ Count      <dbl> 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1,…

Veja que, neste caso, as séries não são aninhadas (poderiamos dividir a série por Genero, por estado, por situação legal). Não existe uma única forma de criar as hierarquias.

prison_gts <- prison |>
  aggregate_key(Gender * Legal * State, Count = sum(Count)/1e3)
glimpse(prison_gts)
Rows: 3,888
Columns: 5
Key: Gender, Legal, State [81]
$ Quarter <qtr> 2005 Q1, 2005 Q2, 2005 Q3, 2005 Q4, 20…
$ Gender  <chr*> <aggregated>, <aggregated>, <aggregat…
$ Legal   <chr*> <aggregated>, <aggregated>, <aggregat…
$ State   <chr*> <aggregated>, <aggregated>, <aggregat…
$ Count   <dbl> 24.296, 24.643, 24.511, 24.393, 24.524…

Observação: a sintaxe Gender * Legal * State indica que não existe uma hierarquia mas que são níveis cruzados.

p1 <- prison_gts |>
  filter(!is_aggregated(Gender), is_aggregated(Legal),
         is_aggregated(State)) |>
  autoplot(Count) +
  theme(legend.position = "none") + 
  labs(y = "Número de pressos ('000)") + ggtitle("Gender")
p2 <- prison_gts |>
  filter(is_aggregated(Gender), !is_aggregated(Legal),
         is_aggregated(State)) |>
  autoplot(Count) +
  theme(legend.position = "none") + 
  labs(y = "Número de pressos ('000)") + ggtitle("Legal")
p3 <- prison_gts |>
  filter(is_aggregated(Gender), is_aggregated(Legal),
         !is_aggregated(State)) |>
  autoplot(Count) +
  theme(legend.position = "none") + 
  labs(y = "Número de pressos ('000)") + ggtitle("State")
p4 <- prison_gts |>
  filter(is_aggregated(Gender), is_aggregated(Legal),
         is_aggregated(State)) |>
  autoplot(Count) +
  theme(legend.position = "none") + 
  labs(y = "Número de pressos ('000)") + ggtitle("Total")
p4 / (p1 + p2 + p3)

Este tipo de dados de séries temporais são conhecidos como séries temporais agrupadas (não podem ser desagregadas de uma única forma).

Utilizando a mesma notação definida anteriormente,

\[y_t = y_{A, t} + y_{B, t} \quad ou \quad y_t = y_{X, t} + y_{Y, t}\]

\[y_t = \underbrace{y_{AX, t} + y_{AY, t}}_{y_{A,t}} + \underbrace{y_{BX, t} + y_{BY, t}}_{y_{B,t}}\quad ou \quad y_t = \underbrace{y_{AX, t} + y_{BX, t}}_{y_{X,t}}+ \underbrace{y_{AY, t} + y_{BY, t}}_{y_{Y, t}}\]

É possível também que seja possível desagregar se forma mista (algumas níveis são desagregados hierarquicamente mas existem outros que são cruzados)

tourism_full <- tourism |>
  aggregate_key((State/Region) * Purpose, Trips = sum(Trips))
glimpse(tourism_full)
Rows: 34,000
Columns: 5
Key: State, Purpose, Region [425]
$ Quarter <qtr> 1998 Q1, 1998 Q2, 1998 Q3, 1998 Q4, 19…
$ State   <chr*> <aggregated>, <aggregated>, <aggregat…
$ Purpose <chr*> <aggregated>, <aggregated>, <aggregat…
$ Region  <chr*> <aggregated>, <aggregated>, <aggregat…
$ Trips   <dbl> 23182.20, 20323.38, 19826.64, 20830.13…
tourism_full |>
  filter(!is_aggregated(Purpose), is_aggregated(State)) |>
  autoplot(Trips) +
  labs(y = "Trips ('000)",
       title = "Australian tourism: purpose") +
  facet_wrap(vars(Purpose), scales = "free_y", ncol = 2) +
  theme(legend.position = "none")

tourism_full |>
  filter(!is_aggregated(Purpose), !is_aggregated(State), is_aggregated(Region)) |>
  autoplot(Trips) +
  labs(y = "Trips ('000)",
       title = "Australian tourism: purpose") +
  facet_wrap(vars(State), scales = "free_y", ncol = 3) +
  theme(legend.position = "none")

Como podemos lidar com esse tipo de dados de séries temporais?

Abordagens de nível único

Isto implica selecionar um nível de agregação e gerar previsões para cada uma das séries nesse nível. Estas previsões são agregados para níveis mais altos, ou desagregados para níveis mais baixos (desta forma obtemos previsões coerentes para o resto da estrutura).

Este tipo de modelagem pode ser feito de cima para abaixo (top-down)ou de baixo para cima (bottom-up).

De baixo pra cima

Fazer previsão para cada uma das séries no nível mais baixo e depois ir somando as previsões para os níveis subsequantes até chegar ao nível mais alto (total).

Pense na série de turismo. Vamos supor que previsões por regiões não são interessantes e apenas utilizar estados.

tourism_states <- tourism |>
  aggregate_key(State, Trips = sum(Trips))
glimpse(tourism_states)
Rows: 720
Columns: 3
Key: State [9]
$ Quarter <qtr> 1998 Q1, 1998 Q2, 1998 Q3, 1998 Q4, 19…
$ State   <chr*> <aggregated>, <aggregated>, <aggregat…
$ Trips   <dbl> 23182.20, 20323.38, 19826.64, 20830.13…
fcasts_state <- tourism_states |>
  filter(!is_aggregated(State)) |>
  model(ets = ETS(Trips)) |>
  forecast(h = 5)
fcasts_state
fcasts_national <- fcasts_state |>
  summarise(value = sum(Trips), .mean = mean(value))
fcasts_national

Em geral, se tivermos vários níveis, podemos utilizar

tourism_states |>
  model(ets = ETS(Trips)) |>
  reconcile(bu = bottom_up(ets)) |>
  forecast(h = 5)

A vantagem desta abordagem é que estamos utilizando o nível mais baixo da séries (ou seja, nenhuma informação é perdida pela agregação). Por outro lado, esta abordagem ignora a relação entre as séries e se o nível mais baixo da série for altamente desagregado pode ser dificil de modelar (pouco sinal, muito ruido).

De cima pra baixo

Consiste em fazer a previsão para o nível mais alto (o total) e depois desagregar a série para os níveis mais baixos.

Sejam \(p_1, \cdots, p_m\) as proporções de desagragação nos níveis mais baixos, ou seja, como a previsão do total será desagregada nos níveis mais baixos. Por exemplo, na seguinte hierarquia

\[y_{AA, t} = p_{AA}y_t, \quad y_{AB, t} = p_{AB}y_t, \quad y_{AC, t} = p_{AC}y_t, \quad y_{BA, t} = p_{BA}y_t, \quad y_{BB, t} = p_{BB}y_t.\]

Uma vez que as previsões dos níveis mais baixos são obtidas, basta agregar elas para obter previsões coerentes nos níveis intermediários.

Como calcular as proporções?

Média das proporções históricas

\[p_j = \dfrac{1}{T} \displaystyle \sum_{t = 1}^T \dfrac{y_{j,t}}{y_t}\]

Proporção da média histórica

\[p_j = \dfrac{\displaystyle \sum_{t = 1}^T \dfrac{y_{j,t}}{T}}{\displaystyle \sum_{t = 1}^T \dfrac{y_t}{T}}\]

Ambos os métodos consideram que a proporção de desagregação é constante ao longo do tempo, o que em alguns casos pode estar longe da realidade. Para contornar este problema, a proposta de Athanasopoulos et al. 2009 pode ser utilizada.

Para ilustrar como o método funciona, pense em uma série com apenas um nível de hierarquia:

  1. Faça a previsão \(h\) passos à frente para cada umas das séries (estás previsões serão apenas utilizadas para calcular a proporção de desagregação).
  2. Calculamos a proporção de cada previsão \(h\) passos à frente no nível inferior w.r.t o agregado de todas as previsões nesse nível.
  3. Essas proporções serão chamadas de proporções de previsão e são as que serão utilizadas para desagregar a série no nível mais baixo.

Em geral, para uma série com \(K\)-níveis hierárquicos, obtemos as proporções de previsão como \[p_j = \displaystyle \prod_{l = 1}^{K} \dfrac{\hat{y}_{j,h}^{(l)}}{\hat{S}_{j,h}^{(l+1)}},\] em que \(\hat{y}_{j,h}^{(l)}\) é a previsão \(h\) passos à frente das séries no nível \(l\) e \(\hat{S}_{j, h}^{(l+1)}\) é a previsão agregada (do nó \(j\)) \(h\) passos à frente da série no nível superior (\(l + 1\)).

A vantagem do método de “cima pra baixo” é que produz boas previsões para os níveis agregados, principalmente quando temos poucas observações nos níveis mais baixos. A desvantagem é que perdemos as particularidades das séries individuais nos níveis mais baixos e produz previsões coeherentes viesadas (Hyndman et al. 2011).

Todos esses métodos estão implementados no R:

Método R
Média das proporções históricias top_down(method = "average_proportions")
Proporção das médias históricias top_down(method = "proportion_averages")
Proporção das previsões top_down(method = "forecast_proportions")

Forecast Reconciliation.

Forecast reconciliation é o processo de ajustar as previsões para que elas sejam coerentes, ou seja, que a previsão dos níveis agregados seja igual à soma das previsões dos níveis desagregados.

Pense nas equações que definem as séries hierárquicas

\[y_t = \underbrace{y_{AA, t} + y_{AB, t} + y_{AC, t}}_{y_{A, t}} + \underbrace{y_{BA, t} + y_{BB, t}}_{y_{B, t}}.\]

Podemos reescrever todas as relações através de uma forma matricial:

\[\underbrace{\begin{bmatrix} y_t \\ y_{A,t}\\ y_{B,t} \\ y_{AA, t} \\ y_{AB, t} \\ y_{AC, t} \\ y_{BA, t} \\ y_{BB, t} \end{bmatrix}}_{\textbf{y}_t} = \underbrace{\begin{bmatrix} 1 & 1 & 1 & 1 & 1 \\ 1 & 1 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 & 1 \\ 1 & 0 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 0 & 1 \end{bmatrix}}_{\textbf{S}} \times \underbrace{\begin{bmatrix} y_{AA, t} \\ y_{AB, t} \\ y_{AC, t} \\ y_{BA, t} \\ y_{BB, t} \end{bmatrix}}_{\textbf{b}_t}\]

De forma semelhante, podemos reescrever as equações que definem as séries agrupadas como

\[\underbrace{\begin{bmatrix} y_t \\ y_{A,t}\\ y_{B,t} \\ y_{X,t}\\ y_{Y,t} \\ y_{AX, t} \\ y_{AY, t} \\ y_{AC, t} \\ y_{BX, t} \\ y_{BY, t} \end{bmatrix}}_{\textbf{y}_t} = \underbrace{\begin{bmatrix} 1 & 1 & 1 & 1 \\ 1 & 1 & 0 & 0 \\ 0 & 0 & 1 & 1 \\ 1 & 0 & 1 & 0 \\ 0 & 1 & 0 & 1 \\ 1 & 0 & 0 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 0 & 0 & 1\\ \end{bmatrix}}_{\textbf{S}} \times \underbrace{\begin{bmatrix} y_{AX, t} \\ y_{AY, t} \\ y_{BX, t} \\ y_{BY, t} \end{bmatrix}}_{\textbf{b}_t}\]

Esta notação matricial permite que utilizemos uma única notação para séries hierárquicas ou agrupadas.

Suponha que você faz a previsão para todas as séries ignorando as restrições de agregação (ou seja, as previsões não são coerentes).

Então, todas as previsões coerentes podem ser obtidas através de

\[\tilde{\textbf{y}}_{t+h} = \textbf{S} \textbf{G} \hat{\textbf{y}}_{t+h},\]

A matriz \(\textbf{G}\) deve ser escolhida de forma apropriada. Por exemplo, se a abordagem for de baixo pra cima, definimos, para o caso do nosso exemplo,

\[\textbf{G} = \begin{bmatrix} 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 \\ \end{bmatrix}\]

Se a abordagem for de cima pra baixo, \[\textbf{G} = \begin{bmatrix} p_1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ p_2 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ p_3 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ p_4 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ p_5 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ \end{bmatrix}\]

Assim, pre-multiplicando as previsões base por \(\textbf{SG}\), temos um conjunto de previsões coerentes.

Até aqui, os métodos descritos apenas consideram previsões de um único nível para depois agregar ou desagregar e obter as previsões para todos os outros níveis. Contudo, em geral, poderiamos utilizar outras matrizes \(\textbf{G}\) e então combinar \(\textbf{SG}\) para obter todas as previsões coerentes. De fato, podemos obter uma matriz \(\textbf{G}\) ótima para termos previsões mais acuradas.

O método MinT (Minimum Trace)

Wickramasuriya et al. 2019 propuseram um método para obter a matriz \(\textbf{G}\) de forma que a variância das previsões coerentes seja mínima.

Suponha que para obter previsões coerentes utilizamos \[\tilde{\textbf{y}}_{t+h} = \textbf{SG} \hat{\textbf{y}}_{t+h}.\]

  1. Gostariamos que as previsões sejam não viesadas. Se as previsões base são não viesadas, então \(\tilde{\textbf{y}}_{t+h}\) serão não viesadas se \(\textbf{SGS} = \textbf{S}\) (nas abordagem de cima pra baixo, isto nunca acotece. Ou seja, essa abordagem sempre fornecerá previsões viesadas).
  2. Precisamos calcular a variância da previsão. A matriz de covariância do erro da previsão \(h\) passos à frente é dada por \[\textbf{V}_h = \mathbb{V}(\textbf{y}_{t+h} - \tilde{\textbf{y}}_{t+h}) = \textbf{SGW}_h\textbf{G'S'},\] em que \(\textbf{W}_h = \mathbb{V}(\textbf{y}_{t+h} - \hat{\textbf{y}}_{t+h}).\)
  3. O objetivo é encontrar \(\textbf{G}\) de forma que o erro de previsão das previsões coerentes seja mínima. Estes erros de previsão estão na diagonal da matriz \(\textbf{V}_h\) e soma desses erros é o traço de \(\textbf{V}_h\).

Wickramasuriya et al. 2019 mostram que a matriz \(\textbf{G}\) que minimiza o traço de \(\textbf{V}_h\) sujeita à restrição de que \(\textbf{SGS} = \textbf{S}\) é dada por \[\textbf{G} = (\textbf{S}' \textbf{W}_{h}^{-1}\textbf{S})^{-1}\textbf{S}'\textbf{W}_h^{-1}.\]

Assim, a previsão ótima é dado por \[\tilde{\textbf{y}}_{t+h} = \textbf{SG}\hat{\textbf{y}}_{t+h} = \textbf{S}(\textbf{S}' \textbf{W}_{h}^{-1}\textbf{S})^{-1}\textbf{S}'\textbf{W}_h^{-1}\hat{\textbf{y}}_{t+h}.\]

Note que para poder utilizar a fórmula anterior na prática, precismos conhecer \(\textbf{W}_{h}\). Existem várias formas de fornecer uma aproximação desta matriz, entre elas:

  1. \(\textbf{W}_h = k_h \textbf{I}\) com \(k_h > 0\). Note que, fazendo \(\textbf{X} = \textbf{S}\) e \(\textbf{y} = \hat{\textbf{y}}\) temos o estimador MQO clássico do modelo de regressão. method = "ols".
  2. \(\textbf{W}_h = k_h diag(\widehat{\textbf{W}}_1)\) com \(k_h > 0\) e \[\widehat{\textbf{W}}_1 = \dfrac{1}{T}\displaystyle \sum_{t = 1}^T \textbf{e}_t \textbf{e}_t',\] em que \(\textbf{e}_t\) é um vetor de resíduos dos modelos que geraram as previsões base. Esta especificação pondera as previsões base utilizando a variância dos resíduos, sendo conhecido como mínimos quadrados ponderados utilizando a variância. method = "wls_var".
  3. \(\textbf{W}_h = k_h \Lambda\) com \(k_h > 0\), \(\Lambda = diag(\textbf{S1})\) e \(\textbf{1}\) é um vetor de uns de tamanho \(m\) (número de séries no nível mais baixo). Note que \(\Lambda\) contém o número de variâncias do erro contribuindo para cada nó. Assim, este estimador depende da estrutura de agregação e não dos dados, sendo chamado de structural scaling. method = "wls_struct".
  4. \(\textbf{W}_h = k_h \textbf{W}_1\) com \(k_h > 0\). A unica suposição deste estimador é que o erro da matriz de covaiância é proporcional um do outro. Podemos utilizar a mariz de covariância amostral (method = "mint_cov") ou, quando \(m\) for grande, estimadores shrinkage (method = "mint_shrink").

Exemplo

tourism_full <- tourism |>
  aggregate_key((State/Region) * Purpose, Trips = sum(Trips))
fit <- tourism_full |>
  filter(year(Quarter) <= 2015) |>
  model(base = ETS(Trips)) |>
  reconcile(
    bu = bottom_up(base),
    ols = min_trace(base, method = "ols"),
    var = min_trace(base, method = "wls_var"),
    stru = min_trace(base, method = "wls_struct"),
    mint_shr = min_trace(base, method = "mint_shrink")
  )
fore <- fit |> forecast(h = 8)
fore |>
  filter(is_aggregated(Region), is_aggregated(Purpose)) |>
  autoplot(
    tourism_full |> filter(year(Quarter) >= 2010),
    level = NULL) +
  facet_wrap(vars(State), scales = "free_y")

fore |>
  filter(is_aggregated(State), !is_aggregated(Purpose)) |>
  autoplot(
    tourism_full |> filter(year(Quarter) >= 2010),
    level = NULL) +
  facet_wrap(vars(Purpose), scales = "free_y")

fore |>
  filter(is_aggregated(State), is_aggregated(Purpose)) |>
  accuracy(data = tourism_full,
           measures = list(rmse = RMSE, mase = MASE)) |>
  group_by(.model) |>
  summarise(rmse = mean(rmse), mase = mean(mase))
fore |>
  filter(is_aggregated(State), !is_aggregated(Purpose)) |>
  accuracy(data = tourism_full,
           measures = list(rmse = RMSE, mase = MASE)) |>
  group_by(.model) |>
  summarise(rmse = mean(rmse), mase = mean(mase))
LS0tCnRpdGxlOiAiU8OpcmllcyBUZW1wb3JhaXMgSGllcsOhcnF1aWNhcyBlIEFncnVwYWRhcyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyMgTW90aXZhw6fDo28KCiMjIyBDYXNlIDE6CgpWb2PDqiBjb21lw6dvdSBhIHRyYWJhbGhhciBubyBtaW5pc3TDqXJpbyBkZSB0dXJpc21vIGUgcmVsYcOnw7VlcyBleHRlcmlvcmVzIGRhIEF1c3Ryw6FsaWEgZSBzdWEgcHJpbWVpcmEgdGFyZWZhIMOpIGZhemVyIGEgcHJldmlzw6NvIGRvIG7Dum1lcm8gZGUgdmlhamFudGVzIHF1ZSBjaGVnYXLDo28gYW8gUGHDrXMgbm8gcHLDs3hpbW8gdHJpbWVzdHJlIChtYXMgdGFtYsOpbSBxdWVyZW0gc2FiZXIgYSBwcmV2aXPDo28gcG9yIGVzdGFkbyBlIHJlZ2nDo28sIHNlIGZvciBwb3Nzw612ZWwpLgoKCiFbQXVzdHJhbGlhIGRpdmlkaWRhIHBvciBlc3RhZG9zXShpbWFnZW5zL2F1c3RyYWxpYS5wbmcpCgpPIGNvbmp1bnRvIGRlIGRhZG9zIGEgc2VyIGFuw6FsaXNhZG8gasOhIGVzdMOhIGRpc3BvbsOtdmVsIG5hIGVtcHJlc2EgZSDDqSBjYXJyZWdhZG8gbm8gKipSKiogY29tIG8gbm9tZSBgdG91cmlzbWAKCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KGZwcDMpCmxpYnJhcnkocGF0Y2h3b3JrKQpnbGltcHNlKHRvdXJpc20pCmBgYAoKCk8gbWluaXN0w6lyaW8gbsOjbyBjb25zaWRlcmEgaW1wb3J0YW50ZSBzZXBhcmFyIG8gbW90aXZvIGRhIHZpYWdlbi4gQXNzaW0gdm9jw6ogcHJlY2lzYSBhZ3JlZ2FyIG9zIGRhZG9zIHBvciByZWdpw6NvIGUgZXN0YWRvLgoKYGBge3J9CnRvdXJpc21faHRzIDwtIHRvdXJpc20gfD4KICBhZ2dyZWdhdGVfa2V5KFN0YXRlIC8gUmVnaW9uLCBUcmlwcyA9IHN1bShUcmlwcykpCnRvdXJpc21faHRzCmBgYAoKCj4gT2JzZXJ2YcOnw6NvOiBhIGZ1bsOnw6NvIGBhZ2dyZWdhdGVfa2V5KClgIHRlbSB1bWEgbm9tZW5jbGF0dXJhIGRlIGBwYWkvZmlsaG9gLgoKCgpgYGB7cn0KdG91cmlzbV9odHMgfD4KICBmaWx0ZXIoaXNfYWdncmVnYXRlZChSZWdpb24pKSB8PgogIGF1dG9wbG90KFRyaXBzKSArCiAgbGFicyh5ID0gIlRyaXBzICgnMDAwKSIsCiAgICAgICB0aXRsZSA9ICJDaGVnYWRhcyBuYSBBdXN0csOhbGlhOiBuYWNpb25hbCBlIHBvciBlc3RhZG9zLiIpICsKICBmYWNldF93cmFwKHZhcnMoU3RhdGUpLCBzY2FsZXMgPSAiZnJlZV95IiwgbmNvbCA9IDMpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKUG9yIGN1cmlvc2lkYWRlLCB2b2PDqiByZXNvbHZlIHZlciBxdWFsIMOpIG8gY29tcG9ydGFtZW50byBkYXMgcmVnacO1ZXMgZGVudHJvIGRlIGNhZGEgZXN0YWRvLgoKCmBgYHtyfQp0b3VyaXNtX2h0cyB8PgogIGZpbHRlcighaXNfYWdncmVnYXRlZChSZWdpb24pKSB8PgogIGF1dG9wbG90KFRyaXBzKSArCiAgbGFicyh5ID0gIlRyaXBzICgnMDAwKSIsCiAgICAgICB0aXRsZSA9ICJDaGVnYWRhcyBuYSBBdXN0csOhbGlhOiBuYWNpb25hbCBlIHBvciBlc3RhZG9zLiIpICsKICBmYWNldF93cmFwKHZhcnMoU3RhdGUpLCBzY2FsZXMgPSAiZnJlZV95IiwgbmNvbCA9IDMpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKCkVzdGUgdGlwbyBkZSBkYWRvcyBkZSBzw6lyaWVzIHRlbXBvcmFpcyBzw6NvIGNvbmhlY2lkb3MgY29tbyBzw6lyaWVzIHRlbXBvcmlzIGhpZXLDoXJxdWljYXMsIHBvaXMgcG9zc3VlbSB1bWEgaGllcmFycXVpYS4KCgohW10oaW1hZ2Vucy9zZXJpZV9oaWVyYXJxdWljYS5wbmcpCgoKClNlICR5X3QkIGRlbm90YXIgbyBuw7ptZXJvIGRlIGNoZWdhZGFzIG5hIEF1c3Ryw6FsaWEgbm8gdGVtcG8gJHQkLCBlICR5X3tqLHR9JCBkZW5vdGFyIG8gbsO6bWVybyBkZSBjaGVnYWRhcyBwYXJhIG8gZXN0YWRvcy9yZWdpw6NvIGRhIEF1c3Ryw6FsaWEgbm8gdGVtcG8gJHQkLiBUZXJpYW1vcyBxdWUKCiQkeV90ID0gXHVuZGVyYnJhY2V7eV97QUEsIHR9ICsgeV97QUIsIHR9ICsgeV97QUMsIHR9fV97eV97QSwgdH19ICsgXHVuZGVyYnJhY2V7eV97QkEsIHR9ICsgeV97QkIsIHR9fV97eV97QiwgdH19JCQKCiMjIyBDYXNlIDI6CgpWb2PDqiBjb21lw6dhIGEgdHJhYmFsaGFyIG5vIGp1ZGljacOhcmlvIGRhIEF1c3Ryw6FsaWEgZSBzdWEgcHJpbWVpcmEgdGFyZWZhIMOpIGZhemVyIHVtYSBwcmV2aXPDo28gZG8gdG90YWwgKG1hcyB0YW1iw6ltLCBzZSBmb3IgcG9zc8OtdmVsLCBwb3Igc2V4bywgc2l0dWHDp8OjbyBsZWdhbCwgZXN0YWRvKSBkZSBwcmVzb3MgcGFyYSBvIHByw7N4aW1vIHRyaW1lc3RyZS4gT3MgZGFkb3MgZXN0w6NvIGRpc3BvbsOtdmVsIG5vIHNlZ3VpbnRlIFtsaW5rXShodHRwczovL09UZXh0cy5jb20vZnBwMy9leHRyYWZpbGVzL3ByaXNvbl9wb3B1bGF0aW9uLmNzdikKCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpwcmlzb24gPC0gcmVhZHI6OnJlYWRfY3N2KCJodHRwczovL09UZXh0cy5jb20vZnBwMy9leHRyYWZpbGVzL3ByaXNvbl9wb3B1bGF0aW9uLmNzdiIpCmdsaW1wc2UocHJpc29uKQpgYGAKCmBgYHtyfQpwcmlzb24kRGF0ZVtjKDEsIDIsIDg1LCA4NiwgNDY5LCA0NzApXQpgYGAKCmBgYHtyfQpwcmlzb24gPC0gcHJpc29uIHw+IAogIG11dGF0ZShRdWFydGVyID0geWVhcnF1YXJ0ZXIoRGF0ZSkpIHw+CiAgc2VsZWN0KC1EYXRlKSAgfD4KICBhc190c2liYmxlKGtleSA9IGMoR2VuZGVyLCBMZWdhbCwgU3RhdGUsIEluZGlnZW5vdXMpLAogICAgICAgICAgICAgaW5kZXggPSBRdWFydGVyKSB8PgogIHJlbG9jYXRlKFF1YXJ0ZXIpCmdsaW1wc2UocHJpc29uKQpgYGAKClZlamEgcXVlLCBuZXN0ZSBjYXNvLCBhcyBzw6lyaWVzIG7Do28gc8OjbyBhbmluaGFkYXMgKHBvZGVyaWFtb3MgZGl2aWRpciBhIHPDqXJpZSBwb3IgR2VuZXJvLCBwb3IgZXN0YWRvLCBwb3Igc2l0dWHDp8OjbyBsZWdhbCkuICoqTsOjbyBleGlzdGUgdW1hIMO6bmljYSBmb3JtYSBkZSBjcmlhciBhcyBoaWVyYXJxdWlhcy4qKgoKYGBge3J9CnByaXNvbl9ndHMgPC0gcHJpc29uIHw+CiAgYWdncmVnYXRlX2tleShHZW5kZXIgKiBMZWdhbCAqIFN0YXRlLCBDb3VudCA9IHN1bShDb3VudCkvMWUzKQpnbGltcHNlKHByaXNvbl9ndHMpCmBgYAoKPiBPYnNlcnZhw6fDo286IGEgc2ludGF4ZSBgR2VuZGVyICogTGVnYWwgKiBTdGF0ZWAgaW5kaWNhIHF1ZSBuw6NvIGV4aXN0ZSB1bWEgaGllcmFycXVpYSBtYXMgcXVlIHPDo28gbsOtdmVpcyBjcnV6YWRvcy4KCmBgYHtyfQpwMSA8LSBwcmlzb25fZ3RzIHw+CiAgZmlsdGVyKCFpc19hZ2dyZWdhdGVkKEdlbmRlciksIGlzX2FnZ3JlZ2F0ZWQoTGVnYWwpLAogICAgICAgICBpc19hZ2dyZWdhdGVkKFN0YXRlKSkgfD4KICBhdXRvcGxvdChDb3VudCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKyAKICBsYWJzKHkgPSAiTsO6bWVybyBkZSBwcmVzc29zICgnMDAwKSIpICsgZ2d0aXRsZSgiR2VuZGVyIikKcDIgPC0gcHJpc29uX2d0cyB8PgogIGZpbHRlcihpc19hZ2dyZWdhdGVkKEdlbmRlciksICFpc19hZ2dyZWdhdGVkKExlZ2FsKSwKICAgICAgICAgaXNfYWdncmVnYXRlZChTdGF0ZSkpIHw+CiAgYXV0b3Bsb3QoQ291bnQpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsgCiAgbGFicyh5ID0gIk7Dum1lcm8gZGUgcHJlc3NvcyAoJzAwMCkiKSArIGdndGl0bGUoIkxlZ2FsIikKcDMgPC0gcHJpc29uX2d0cyB8PgogIGZpbHRlcihpc19hZ2dyZWdhdGVkKEdlbmRlciksIGlzX2FnZ3JlZ2F0ZWQoTGVnYWwpLAogICAgICAgICAhaXNfYWdncmVnYXRlZChTdGF0ZSkpIHw+CiAgYXV0b3Bsb3QoQ291bnQpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsgCiAgbGFicyh5ID0gIk7Dum1lcm8gZGUgcHJlc3NvcyAoJzAwMCkiKSArIGdndGl0bGUoIlN0YXRlIikKcDQgPC0gcHJpc29uX2d0cyB8PgogIGZpbHRlcihpc19hZ2dyZWdhdGVkKEdlbmRlciksIGlzX2FnZ3JlZ2F0ZWQoTGVnYWwpLAogICAgICAgICBpc19hZ2dyZWdhdGVkKFN0YXRlKSkgfD4KICBhdXRvcGxvdChDb3VudCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKyAKICBsYWJzKHkgPSAiTsO6bWVybyBkZSBwcmVzc29zICgnMDAwKSIpICsgZ2d0aXRsZSgiVG90YWwiKQpwNCAvIChwMSArIHAyICsgcDMpCmBgYAoKCkVzdGUgdGlwbyBkZSBkYWRvcyBkZSBzw6lyaWVzIHRlbXBvcmFpcyBzw6NvIGNvbmhlY2lkb3MgY29tbyBzw6lyaWVzIHRlbXBvcmFpcyBhZ3J1cGFkYXMgKG7Do28gcG9kZW0gc2VyIGRlc2FncmVnYWRhcyBkZSB1bWEgw7puaWNhIGZvcm1hKS4KCgohW10oaW1hZ2Vucy9zZXJpZXNfYWdydXBhZGFzLnBuZykKClV0aWxpemFuZG8gYSBtZXNtYSBub3Rhw6fDo28gZGVmaW5pZGEgYW50ZXJpb3JtZW50ZSwgCgokJHlfdCA9IHlfe0EsIHR9ICsgeV97QiwgdH0gXHF1YWQgb3UgXHF1YWQgeV90ID0geV97WCwgdH0gKyB5X3tZLCB0fSQkCgokJHlfdCA9IFx1bmRlcmJyYWNle3lfe0FYLCB0fSArIHlfe0FZLCB0fX1fe3lfe0EsdH19ICArIFx1bmRlcmJyYWNle3lfe0JYLCB0fSArIHlfe0JZLCB0fX1fe3lfe0IsdH19XHF1YWQgb3UgXHF1YWQgeV90ID0gXHVuZGVyYnJhY2V7eV97QVgsIHR9ICsgeV97QlgsIHR9fV97eV97WCx0fX0rIFx1bmRlcmJyYWNle3lfe0FZLCB0fSArIHlfe0JZLCB0fX1fe3lfe1ksIHR9fSQkCgoKPiDDiSBwb3Nzw612ZWwgdGFtYsOpbSBxdWUgc2VqYSBwb3Nzw612ZWwgZGVzYWdyZWdhciBzZSBmb3JtYSBtaXN0YSAoYWxndW1hcyBuw612ZWlzIHPDo28gZGVzYWdyZWdhZG9zIGhpZXJhcnF1aWNhbWVudGUgbWFzIGV4aXN0ZW0gb3V0cm9zIHF1ZSBzw6NvIGNydXphZG9zKQoKCmBgYHtyfQp0b3VyaXNtX2Z1bGwgPC0gdG91cmlzbSB8PgogIGFnZ3JlZ2F0ZV9rZXkoKFN0YXRlL1JlZ2lvbikgKiBQdXJwb3NlLCBUcmlwcyA9IHN1bShUcmlwcykpCmdsaW1wc2UodG91cmlzbV9mdWxsKQpgYGAKCgpgYGB7cn0KdG91cmlzbV9mdWxsIHw+CiAgZmlsdGVyKCFpc19hZ2dyZWdhdGVkKFB1cnBvc2UpLCBpc19hZ2dyZWdhdGVkKFN0YXRlKSkgfD4KICBhdXRvcGxvdChUcmlwcykgKwogIGxhYnMoeSA9ICJUcmlwcyAoJzAwMCkiLAogICAgICAgdGl0bGUgPSAiQXVzdHJhbGlhbiB0b3VyaXNtOiBwdXJwb3NlIikgKwogIGZhY2V0X3dyYXAodmFycyhQdXJwb3NlKSwgc2NhbGVzID0gImZyZWVfeSIsIG5jb2wgPSAyKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCgpgYGB7cn0KdG91cmlzbV9mdWxsIHw+CiAgZmlsdGVyKCFpc19hZ2dyZWdhdGVkKFB1cnBvc2UpLCAhaXNfYWdncmVnYXRlZChTdGF0ZSksIGlzX2FnZ3JlZ2F0ZWQoUmVnaW9uKSkgfD4KICBhdXRvcGxvdChUcmlwcykgKwogIGxhYnMoeSA9ICJUcmlwcyAoJzAwMCkiLAogICAgICAgdGl0bGUgPSAiQXVzdHJhbGlhbiB0b3VyaXNtOiBwdXJwb3NlIikgKwogIGZhY2V0X3dyYXAodmFycyhTdGF0ZSksIHNjYWxlcyA9ICJmcmVlX3kiLCBuY29sID0gMykgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgoKKipDb21vIHBvZGVtb3MgbGlkYXIgY29tIGVzc2UgdGlwbyBkZSBkYWRvcyBkZSBzw6lyaWVzIHRlbXBvcmFpcz8qKgoKCiMjIEFib3JkYWdlbnMgZGUgbsOtdmVsIMO6bmljbwoKSXN0byBpbXBsaWNhIHNlbGVjaW9uYXIgdW0gbsOtdmVsIGRlIGFncmVnYcOnw6NvIGUgZ2VyYXIgcHJldmlzw7VlcyBwYXJhIGNhZGEgdW1hIGRhcyBzw6lyaWVzIG5lc3NlIG7DrXZlbC4gRXN0YXMgcHJldmlzw7VlcyBzw6NvIGFncmVnYWRvcyBwYXJhIG7DrXZlaXMgbWFpcyBhbHRvcywgb3UgZGVzYWdyZWdhZG9zIHBhcmEgbsOtdmVpcyBtYWlzIGJhaXhvcyAoZGVzdGEgZm9ybWEgb2J0ZW1vcyBwcmV2aXPDtWVzIGNvZXJlbnRlcyBwYXJhIG8gcmVzdG8gZGEgZXN0cnV0dXJhKS4gCgpFc3RlIHRpcG8gZGUgbW9kZWxhZ2VtIHBvZGUgc2VyIGZlaXRvIGRlIGNpbWEgcGFyYSBhYmFpeG8gKHRvcC1kb3duKW91IGRlIGJhaXhvIHBhcmEgY2ltYSAoYm90dG9tLXVwKS4KCiMjIyBEZSBiYWl4byBwcmEgY2ltYQoKRmF6ZXIgcHJldmlzw6NvIHBhcmEgY2FkYSB1bWEgZGFzIHPDqXJpZXMgbm8gbsOtdmVsIG1haXMgYmFpeG8gZSBkZXBvaXMgaXIgc29tYW5kbyBhcyBwcmV2aXPDtWVzIHBhcmEgb3MgbsOtdmVpcyBzdWJzZXF1YW50ZXMgYXTDqSBjaGVnYXIgYW8gbsOtdmVsIG1haXMgYWx0byAodG90YWwpLgoKClBlbnNlIG5hIHPDqXJpZSBkZSB0dXJpc21vLiBWYW1vcyBzdXBvciBxdWUgcHJldmlzw7VlcyBwb3IgcmVnacO1ZXMgbsOjbyBzw6NvIGludGVyZXNzYW50ZXMgZSBhcGVuYXMgdXRpbGl6YXIgZXN0YWRvcy4KCmBgYHtyfQp0b3VyaXNtX3N0YXRlcyA8LSB0b3VyaXNtIHw+CiAgYWdncmVnYXRlX2tleShTdGF0ZSwgVHJpcHMgPSBzdW0oVHJpcHMpKQpnbGltcHNlKHRvdXJpc21fc3RhdGVzKQpgYGAKCgoKYGBge3J9CmZjYXN0c19zdGF0ZSA8LSB0b3VyaXNtX3N0YXRlcyB8PgogIGZpbHRlcighaXNfYWdncmVnYXRlZChTdGF0ZSkpIHw+CiAgbW9kZWwoZXRzID0gRVRTKFRyaXBzKSkgfD4KICBmb3JlY2FzdChoID0gNSkKZmNhc3RzX3N0YXRlCmBgYAoKCgpgYGB7cn0KZmNhc3RzX25hdGlvbmFsIDwtIGZjYXN0c19zdGF0ZSB8PgogIHN1bW1hcmlzZSh2YWx1ZSA9IHN1bShUcmlwcyksIC5tZWFuID0gbWVhbih2YWx1ZSkpCmZjYXN0c19uYXRpb25hbApgYGAKCgoKRW0gZ2VyYWwsIHNlIHRpdmVybW9zIHbDoXJpb3MgbsOtdmVpcywgcG9kZW1vcyB1dGlsaXphcgoKYGBge3J9CnRvdXJpc21fc3RhdGVzIHw+CiAgbW9kZWwoZXRzID0gRVRTKFRyaXBzKSkgfD4KICByZWNvbmNpbGUoYnUgPSBib3R0b21fdXAoZXRzKSkgfD4KICBmb3JlY2FzdChoID0gNSkKYGBgCgoKCgo+IEEgdmFudGFnZW0gZGVzdGEgYWJvcmRhZ2VtIMOpIHF1ZSBlc3RhbW9zIHV0aWxpemFuZG8gbyBuw612ZWwgbWFpcyBiYWl4byBkYSBzw6lyaWVzIChvdSBzZWphLCBuZW5odW1hIGluZm9ybWHDp8OjbyDDqSBwZXJkaWRhIHBlbGEgYWdyZWdhw6fDo28pLiBQb3Igb3V0cm8gbGFkbywgZXN0YSBhYm9yZGFnZW0gaWdub3JhIGEgcmVsYcOnw6NvIGVudHJlIGFzIHPDqXJpZXMgZSBzZSBvIG7DrXZlbCBtYWlzIGJhaXhvIGRhIHPDqXJpZSBmb3IgX2FsdGFtZW50ZSBkZXNhZ3JlZ2Fkb18gIHBvZGUgc2VyIGRpZmljaWwgZGUgbW9kZWxhciAocG91Y28gc2luYWwsIG11aXRvIHJ1aWRvKS4KCgoKIyMjIERlIGNpbWEgcHJhIGJhaXhvCgpDb25zaXN0ZSBlbSBmYXplciBhIHByZXZpc8OjbyBwYXJhIG8gbsOtdmVsIG1haXMgYWx0byAobyB0b3RhbCkgZSBkZXBvaXMgZGVzYWdyZWdhciBhIHPDqXJpZSBwYXJhIG9zIG7DrXZlaXMgbWFpcyBiYWl4b3MuCgpTZWphbSAkcF8xLCBcY2RvdHMsIHBfbSQgYXMgcHJvcG9yw6fDtWVzIGRlIGRlc2FncmFnYcOnw6NvIG5vcyBuw612ZWlzIG1haXMgYmFpeG9zLCBvdSBzZWphLCBjb21vIGEgcHJldmlzw6NvIGRvIHRvdGFsIHNlcsOhIGRlc2FncmVnYWRhIG5vcyBuw612ZWlzIG1haXMgYmFpeG9zLiBQb3IgZXhlbXBsbywgbmEgc2VndWludGUgaGllcmFycXVpYQoKIVtdKGltYWdlbnMvc2VyaWVfaGllcmFycXVpY2EucG5nKQoKCgokJHlfe0FBLCB0fSA9IHBfe0FBfXlfdCwgXHF1YWQgeV97QUIsIHR9ID0gcF97QUJ9eV90LCBccXVhZCB5X3tBQywgdH0gPSBwX3tBQ315X3QsIFxxdWFkIHlfe0JBLCB0fSA9IHBfe0JBfXlfdCwgXHF1YWQgeV97QkIsIHR9ID0gcF97QkJ9eV90LiQkCgpVbWEgdmV6IHF1ZSBhcyBwcmV2aXPDtWVzIGRvcyBuw612ZWlzIG1haXMgYmFpeG9zIHPDo28gb2J0aWRhcywgYmFzdGEgYWdyZWdhciBlbGFzIHBhcmEgb2J0ZXIgcHJldmlzw7VlcyBjb2VyZW50ZXMgbm9zIG7DrXZlaXMgaW50ZXJtZWRpw6FyaW9zLgoKKipDb21vIGNhbGN1bGFyIGFzIHByb3BvcsOnw7Vlcz8qKgoKIyMjIyBNw6lkaWEgZGFzIHByb3BvcsOnw7VlcyBoaXN0w7NyaWNhcwoKJCRwX2ogPSBcZGZyYWN7MX17VH0gXGRpc3BsYXlzdHlsZSBcc3VtX3t0ID0gMX1eVCBcZGZyYWN7eV97aix0fX17eV90fSQkCgojIyMjIFByb3BvcsOnw6NvIGRhIG3DqWRpYSBoaXN0w7NyaWNhCgokJHBfaiA9IFxkZnJhY3tcZGlzcGxheXN0eWxlIFxzdW1fe3QgPSAxfV5UIFxkZnJhY3t5X3tqLHR9fXtUfX17XGRpc3BsYXlzdHlsZSBcc3VtX3t0ID0gMX1eVCBcZGZyYWN7eV90fXtUfX0kJAoKCgoKQW1ib3Mgb3MgbcOpdG9kb3MgY29uc2lkZXJhbSBxdWUgYSBwcm9wb3LDp8OjbyBkZSBkZXNhZ3JlZ2HDp8OjbyDDqSBjb25zdGFudGUgYW8gbG9uZ28gZG8gdGVtcG8sIG8gcXVlIGVtIGFsZ3VucyBjYXNvcyBwb2RlIGVzdGFyIGxvbmdlIGRhIHJlYWxpZGFkZS4gUGFyYSBjb250b3JuYXIgZXN0ZSBwcm9ibGVtYSwgYSBwcm9wb3N0YSBkZSBbQXRoYW5hc29wb3Vsb3MgZXQgYWwuIDIwMDldKGh0dHBzOi8vd3d3LnNjaWVuY2VkaXJlY3QuY29tL3NjaWVuY2UvYXJ0aWNsZS9hYnMvcGlpL1MwMTY5MjA3MDA4MDAwNjkxKSBwb2RlIHNlciB1dGlsaXphZGEuIAoKUGFyYSBpbHVzdHJhciBjb21vIG8gbcOpdG9kbyBmdW5jaW9uYSwgcGVuc2UgZW0gdW1hIHPDqXJpZSBjb20gYXBlbmFzIHVtIG7DrXZlbCBkZSBoaWVyYXJxdWlhOgoKMS4gRmHDp2EgYSBwcmV2aXPDo28gJGgkIHBhc3NvcyDDoCBmcmVudGUgcGFyYSBjYWRhIHVtYXMgZGFzIHPDqXJpZXMgKGVzdMOhcyBwcmV2aXPDtWVzIHNlcsOjbyBhcGVuYXMgdXRpbGl6YWRhcyBwYXJhIGNhbGN1bGFyIGEgcHJvcG9yw6fDo28gZGUgZGVzYWdyZWdhw6fDo28pLgoyLiBDYWxjdWxhbW9zIGEgcHJvcG9yw6fDo28gZGUgY2FkYSBwcmV2aXPDo28gJGgkIHBhc3NvcyDDoCBmcmVudGUgbm8gbsOtdmVsIGluZmVyaW9yIHcuci50IG8gYWdyZWdhZG8gZGUgdG9kYXMgYXMgcHJldmlzw7VlcyBuZXNzZSBuw612ZWwuCjMuIEVzc2FzIHByb3BvcsOnw7VlcyBzZXLDo28gY2hhbWFkYXMgZGUgcHJvcG9yw6fDtWVzIGRlIHByZXZpc8OjbyBlIHPDo28gYXMgcXVlIHNlcsOjbyB1dGlsaXphZGFzIHBhcmEgZGVzYWdyZWdhciBhIHPDqXJpZSBubyBuw612ZWwgbWFpcyBiYWl4by4KCgpFbSBnZXJhbCwgcGFyYSB1bWEgc8OpcmllIGNvbSAkSyQtbsOtdmVpcyBoaWVyw6FycXVpY29zLCBvYnRlbW9zIGFzIHByb3BvcsOnw7VlcyBkZSBwcmV2aXPDo28gY29tbyAkJHBfaiA9IFxkaXNwbGF5c3R5bGUgXHByb2Rfe2wgPSAxfV57S30gXGRmcmFje1xoYXR7eX1fe2osaH1eeyhsKX19e1xoYXR7U31fe2osaH1eeyhsKzEpfX0sJCQgZW0gcXVlICRcaGF0e3l9X3tqLGh9XnsobCl9JCDDqSBhIHByZXZpc8OjbyAkaCQgcGFzc29zIMOgIGZyZW50ZSBkYXMgc8OpcmllcyBubyBuw612ZWwgJGwkIGUgJFxoYXR7U31fe2osIGh9XnsobCsxKX0kIMOpIGEgcHJldmlzw6NvIGFncmVnYWRhIChkbyBuw7MgJGokKSAkaCQgcGFzc29zIMOgIGZyZW50ZSBkYSBzw6lyaWUgbm8gbsOtdmVsIHN1cGVyaW9yICgkbCArIDEkKS4KIAoKCj4gKipBIHZhbnRhZ2VtIGRvIG3DqXRvZG8gZGUgImNpbWEgcHJhIGJhaXhvIiDDqSBxdWUgcHJvZHV6IGJvYXMgcHJldmlzw7VlcyBwYXJhIG9zIG7DrXZlaXMgYWdyZWdhZG9zLCBwcmluY2lwYWxtZW50ZSBxdWFuZG8gdGVtb3MgcG91Y2FzIG9ic2VydmHDp8O1ZXMgbm9zIG7DrXZlaXMgbWFpcyBiYWl4b3MuIEEgZGVzdmFudGFnZW0gw6kgcXVlIHBlcmRlbW9zIGFzIHBhcnRpY3VsYXJpZGFkZXMgZGFzIHPDqXJpZXMgaW5kaXZpZHVhaXMgbm9zIG7DrXZlaXMgbWFpcyBiYWl4b3MgZSBwcm9kdXogcHJldmlzw7VlcyBjb2VoZXJlbnRlcyB2aWVzYWRhcyBbKEh5bmRtYW4gZXQgYWwuIDIwMTEpXShodHRwczovL3d3dy5zY2llbmNlZGlyZWN0LmNvbS9zY2llbmNlL2FydGljbGUvcGlpL1MwMTY3OTQ3MzExMDAwOTcxKS4qKgoKClRvZG9zIGVzc2VzIG3DqXRvZG9zIGVzdMOjbyBpbXBsZW1lbnRhZG9zIG5vIFI6Cgp8IE3DqXRvZG8gICAgICAgfCAgICBSICAgICAgICAgICAgfAp8Oi0tLS0tLS0tLS0tLTp8Oi0tLS0tLS0tLS0tLS0tLTp8CnwgTcOpZGlhIGRhcyBwcm9wb3LDp8O1ZXMgaGlzdMOzcmljaWFzIHwgICBgdG9wX2Rvd24obWV0aG9kID0gImF2ZXJhZ2VfcHJvcG9ydGlvbnMiKWAgICAgICAgICAgfAp8IFByb3BvcsOnw6NvIGRhcyBtw6lkaWFzIGhpc3TDs3JpY2lhcyB8ICAgYHRvcF9kb3duKG1ldGhvZCA9ICJwcm9wb3J0aW9uX2F2ZXJhZ2VzIilgICAgICAgICAgIHwKfCBQcm9wb3LDp8OjbyBkYXMgcHJldmlzw7VlcyB8ICAgYHRvcF9kb3duKG1ldGhvZCA9ICJmb3JlY2FzdF9wcm9wb3J0aW9ucyIpYCAgICAgICAgICB8CgoKIyMgRm9yZWNhc3QgUmVjb25jaWxpYXRpb24uCgoKX0ZvcmVjYXN0IHJlY29uY2lsaWF0aW9uXyDDqSBvIHByb2Nlc3NvIGRlIGFqdXN0YXIgYXMgcHJldmlzw7VlcyBwYXJhIHF1ZSBlbGFzIHNlamFtIGNvZXJlbnRlcywgb3Ugc2VqYSwgcXVlIGEgcHJldmlzw6NvIGRvcyBuw612ZWlzIGFncmVnYWRvcyBzZWphIGlndWFsIMOgIHNvbWEgZGFzIHByZXZpc8O1ZXMgZG9zIG7DrXZlaXMgZGVzYWdyZWdhZG9zLgoKUGVuc2UgbmFzIGVxdWHDp8O1ZXMgcXVlIGRlZmluZW0gYXMgc8OpcmllcyBoaWVyw6FycXVpY2FzCgohW10oaW1hZ2Vucy9zZXJpZV9oaWVyYXJxdWljYS5wbmcpCgoKJCR5X3QgPSBcdW5kZXJicmFjZXt5X3tBQSwgdH0gKyB5X3tBQiwgdH0gKyB5X3tBQywgdH19X3t5X3tBLCB0fX0gKyBcdW5kZXJicmFjZXt5X3tCQSwgdH0gKyB5X3tCQiwgdH19X3t5X3tCLCB0fX0uJCQKCgpQb2RlbW9zIHJlZXNjcmV2ZXIgdG9kYXMgYXMgcmVsYcOnw7VlcyBhdHJhdsOpcyBkZSB1bWEgZm9ybWEgbWF0cmljaWFsOgoKCgoKCgokJFx1bmRlcmJyYWNle1xiZWdpbntibWF0cml4fQogICAgeV90IFxcCiAgICB5X3tBLHR9XFwKICAgIHlfe0IsdH0gXFwKICAgIHlfe0FBLCB0fSBcXAogICAgeV97QUIsIHR9IFxcCiAgICB5X3tBQywgdH0gXFwKICAgIHlfe0JBLCB0fSBcXAogICAgeV97QkIsIHR9IApcZW5ke2JtYXRyaXh9fV97XHRleHRiZnt5fV90fSA9IFx1bmRlcmJyYWNle1xiZWdpbntibWF0cml4fQogICAgMSAmIDEgJiAxICYgMSAmIDEgXFwKICAgIDEgJiAxICYgMSAmIDAgJiAwIFxcCiAgICAwICYgMCAmIDAgJiAxICYgMSBcXAogICAgMSAmIDAgJiAwICYgMCAmIDAgXFwKICAgIDAgJiAxICYgMCAmIDAgJiAwIFxcCiAgICAwICYgMCAmIDAgJiAxICYgMCBcXAogICAgMCAmIDAgJiAwICYgMCAmIDEgClxlbmR7Ym1hdHJpeH19X3tcdGV4dGJme1N9fSBcdGltZXMgXHVuZGVyYnJhY2V7XGJlZ2lue2JtYXRyaXh9CiAgICB5X3tBQSwgdH0gXFwKICAgIHlfe0FCLCB0fSBcXAogICAgeV97QUMsIHR9IFxcCiAgICB5X3tCQSwgdH0gXFwKICAgIHlfe0JCLCB0fSAKXGVuZHtibWF0cml4fX1fe1x0ZXh0YmZ7Yn1fdH0kJAoKCkRlIGZvcm1hIHNlbWVsaGFudGUsIHBvZGVtb3MgcmVlc2NyZXZlciBhcyBlcXVhw6fDtWVzIHF1ZSBkZWZpbmVtIGFzIHPDqXJpZXMgYWdydXBhZGFzIGNvbW8KCiFbXShpbWFnZW5zL3Nlcmllc19hZ3J1cGFkYXMucG5nKQoKCgokJFx1bmRlcmJyYWNle1xiZWdpbntibWF0cml4fQogICAgeV90IFxcCiAgICB5X3tBLHR9XFwKICAgIHlfe0IsdH0gXFwKICAgIHlfe1gsdH1cXAogICAgeV97WSx0fSBcXAogICAgeV97QVgsIHR9IFxcCiAgICB5X3tBWSwgdH0gXFwKICAgIHlfe0FDLCB0fSBcXAogICAgeV97QlgsIHR9IFxcCiAgICB5X3tCWSwgdH0gClxlbmR7Ym1hdHJpeH19X3tcdGV4dGJme3l9X3R9ID0gXHVuZGVyYnJhY2V7XGJlZ2lue2JtYXRyaXh9CiAgICAxICYgMSAmIDEgJiAxIFxcCiAgICAxICYgMSAmIDAgJiAwIFxcCiAgICAwICYgMCAmIDEgJiAxIFxcCiAgICAxICYgMCAmIDEgJiAwICBcXAogICAgMCAmIDEgJiAwICYgMSBcXAogICAgMSAmIDAgJiAwICYgMFxcCiAgICAwICYgMSAmIDAgJiAwXFwKICAgIDAgJiAwICYgMSAmIDBcXAogICAgMCAmIDAgJiAwICYgMVxcClxlbmR7Ym1hdHJpeH19X3tcdGV4dGJme1N9fSBcdGltZXMgXHVuZGVyYnJhY2V7XGJlZ2lue2JtYXRyaXh9CiAgICB5X3tBWCwgdH0gXFwKICAgIHlfe0FZLCB0fSBcXAogICAgeV97QlgsIHR9IFxcCiAgICB5X3tCWSwgdH0gIApcZW5ke2JtYXRyaXh9fV97XHRleHRiZntifV90fSQkCgpFc3RhIG5vdGHDp8OjbyBtYXRyaWNpYWwgcGVybWl0ZSBxdWUgdXRpbGl6ZW1vcyB1bWEgw7puaWNhIG5vdGHDp8OjbyBwYXJhIHPDqXJpZXMgaGllcsOhcnF1aWNhcyBvdSBhZ3J1cGFkYXMuCgoKU3Vwb25oYSBxdWUgdm9jw6ogZmF6IGEgcHJldmlzw6NvIHBhcmEgdG9kYXMgYXMgc8OpcmllcyBpZ25vcmFuZG8gYXMgcmVzdHJpw6fDtWVzIGRlIGFncmVnYcOnw6NvIChvdSBzZWphLCBhcyBwcmV2aXPDtWVzIG7Do28gc8OjbyBjb2VyZW50ZXMpLiAKCkVudMOjbywgdG9kYXMgYXMgcHJldmlzw7VlcyBjb2VyZW50ZXMgcG9kZW0gc2VyIG9idGlkYXMgYXRyYXbDqXMgZGUgCgokJFx0aWxkZXtcdGV4dGJme3l9fV97dCtofSA9IFx0ZXh0YmZ7U30gXHRleHRiZntHfSBcaGF0e1x0ZXh0YmZ7eX19X3t0K2h9LCQkIAoKCi0gJFx0ZXh0YmZ7R306JCBtYXBlaWEgYXMgcHJldmlzw7VlcyBubyBuw612ZWwgaW5mZXJpb3IKLSAkXHRleHRiZntTfTokIGZheiBhIGFncmVnYcOnw6NvIG5vcyBwcsOzeGltb3MgbsOtdmVpcyBwYXJhIHByb2R1emlyIHByZXZpc8O1ZXMgY29lcmVudGVzLgoKQSBtYXRyaXogJFx0ZXh0YmZ7R30kIGRldmUgc2VyIGVzY29saGlkYSBkZSBmb3JtYSBhcHJvcHJpYWRhLiBQb3IgZXhlbXBsbywgc2UgYSBhYm9yZGFnZW0gZm9yIGRlIGJhaXhvIHByYSBjaW1hLCBkZWZpbmltb3MsIHBhcmEgbyBjYXNvIGRvIG5vc3NvIGV4ZW1wbG8sCgoKJCRcdGV4dGJme0d9ID0gXGJlZ2lue2JtYXRyaXh9CiAgICAwICYgMCAmIDAgJiAxICYgMCAmIDAgJiAwICYgMCBcXAogICAgMCAmIDAgJiAwICYgMCAmIDEgJiAwICYgMCAmIDAgXFwKICAgIDAgJiAwICYgMCAmIDAgJiAwICYgMSAmIDAgJiAwIFxcCiAgICAwICYgMCAmIDAgJiAwICYgMCAmIDAgJiAxICYgMCBcXAogICAgMCAmIDAgJiAwICYgMCAmIDAgJiAwICYgMCAmIDEgXFwKXGVuZHtibWF0cml4fSQkCgpTZSBhIGFib3JkYWdlbSBmb3IgZGUgY2ltYSBwcmEgYmFpeG8sICQkXHRleHRiZntHfSA9IFxiZWdpbntibWF0cml4fQogICAgcF8xICYgMCAmIDAgJiAwICYgMCAmIDAgJiAwICYgMCBcXAogICAgcF8yICYgMCAmIDAgJiAwICYgMCAmIDAgJiAwICYgMCBcXAogICAgcF8zICYgMCAmIDAgJiAwICYgMCAmIDAgJiAwICYgMCBcXAogICAgcF80ICYgMCAmIDAgJiAwICYgMCAmIDAgJiAwICYgMCBcXAogICAgcF81ICYgMCAmIDAgJiAwICYgMCAmIDAgJiAwICYgMCBcXApcZW5ke2JtYXRyaXh9JCQKCkFzc2ltLCBwcmUtbXVsdGlwbGljYW5kbyBhcyBwcmV2aXPDtWVzIGJhc2UgcG9yICRcdGV4dGJme1NHfSQsIHRlbW9zIHVtIGNvbmp1bnRvIGRlIHByZXZpc8O1ZXMgY29lcmVudGVzLgoKCj4gQXTDqSBhcXVpLCBvcyBtw6l0b2RvcyBkZXNjcml0b3MgYXBlbmFzIGNvbnNpZGVyYW0gcHJldmlzw7VlcyBkZSB1bSDDum5pY28gbsOtdmVsIHBhcmEgZGVwb2lzIGFncmVnYXIgb3UgZGVzYWdyZWdhciBlIG9idGVyIGFzIHByZXZpc8O1ZXMgcGFyYSB0b2RvcyBvcyBvdXRyb3MgbsOtdmVpcy4gQ29udHVkbywgZW0gZ2VyYWwsIHBvZGVyaWFtb3MgdXRpbGl6YXIgb3V0cmFzIG1hdHJpemVzICRcdGV4dGJme0d9JCBlIGVudMOjbyBjb21iaW5hciAkXHRleHRiZntTR30kIHBhcmEgb2J0ZXIgdG9kYXMgYXMgcHJldmlzw7VlcyBjb2VyZW50ZXMuIERlIGZhdG8sIHBvZGVtb3Mgb2J0ZXIgdW1hIG1hdHJpeiAkXHRleHRiZntHfSQgw7N0aW1hIHBhcmEgdGVybW9zIHByZXZpc8O1ZXMgbWFpcyBhY3VyYWRhcy4KCiMjIE8gbcOpdG9kbyBNaW5UIChNaW5pbXVtIFRyYWNlKQoKW1dpY2tyYW1hc3VyaXlhIGV0IGFsLiAyMDE5XShodHRwczovL3d3dy50YW5kZm9ubGluZS5jb20vZG9pL2Ficy8xMC4xMDgwLzAxNjIxNDU5LjIwMTguMTQ0ODgyNT9qb3VybmFsQ29kZT11YXNhMjApIHByb3B1c2VyYW0gdW0gbcOpdG9kbyBwYXJhIG9idGVyIGEgbWF0cml6ICRcdGV4dGJme0d9JCBkZSBmb3JtYSBxdWUgYSB2YXJpw6JuY2lhIGRhcyBwcmV2aXPDtWVzIGNvZXJlbnRlcyBzZWphIG3DrW5pbWEuCgoKU3Vwb25oYSBxdWUgcGFyYSBvYnRlciBwcmV2aXPDtWVzIGNvZXJlbnRlcyB1dGlsaXphbW9zICQkXHRpbGRle1x0ZXh0YmZ7eX19X3t0K2h9ID0gXHRleHRiZntTR30gXGhhdHtcdGV4dGJme3l9fV97dCtofS4kJAoKMS4gR29zdGFyaWFtb3MgcXVlIGFzIHByZXZpc8O1ZXMgc2VqYW0gbsOjbyB2aWVzYWRhcy4gU2UgYXMgcHJldmlzw7VlcyBiYXNlIHPDo28gbsOjbyB2aWVzYWRhcywgZW50w6NvICRcdGlsZGV7XHRleHRiZnt5fX1fe3QraH0kIHNlcsOjbyBuw6NvIHZpZXNhZGFzIHNlICRcdGV4dGJme1NHU30gPSBcdGV4dGJme1N9JCAobmFzIGFib3JkYWdlbSBkZSBjaW1hIHByYSBiYWl4bywgaXN0byBudW5jYSBhY290ZWNlLiBPdSBzZWphLCBlc3NhIGFib3JkYWdlbSBzZW1wcmUgZm9ybmVjZXLDoSBwcmV2aXPDtWVzIHZpZXNhZGFzKS4KMi4gUHJlY2lzYW1vcyBjYWxjdWxhciBhIHZhcmnDom5jaWEgZGEgcHJldmlzw6NvLiBBIG1hdHJpeiBkZSBjb3ZhcmnDom5jaWEgZG8gZXJybyBkYSBwcmV2aXPDo28gJGgkIHBhc3NvcyDDoCBmcmVudGUgw6kgZGFkYSBwb3IgJCRcdGV4dGJme1Z9X2ggPSBcbWF0aGJie1Z9KFx0ZXh0YmZ7eX1fe3QraH0gLSBcdGlsZGV7XHRleHRiZnt5fX1fe3QraH0pID0gXHRleHRiZntTR1d9X2hcdGV4dGJme0cnUyd9LCQkIGVtIHF1ZSAkXHRleHRiZntXfV9oID0gXG1hdGhiYntWfShcdGV4dGJme3l9X3t0K2h9IC0gXGhhdHtcdGV4dGJme3l9fV97dCtofSkuJAozLiBPIG9iamV0aXZvIMOpIGVuY29udHJhciAkXHRleHRiZntHfSQgZGUgZm9ybWEgcXVlIG8gZXJybyBkZSBwcmV2aXPDo28gZGFzIHByZXZpc8O1ZXMgY29lcmVudGVzIHNlamEgbcOtbmltYS4gRXN0ZXMgZXJyb3MgZGUgcHJldmlzw6NvIGVzdMOjbyBuYSBkaWFnb25hbCBkYSBtYXRyaXogJFx0ZXh0YmZ7Vn1faCQgZSBzb21hIGRlc3NlcyBlcnJvcyDDqSBvIHRyYcOnbyBkZSAkXHRleHRiZntWfV9oJC4KCgpbV2lja3JhbWFzdXJpeWEgZXQgYWwuIDIwMTldKGh0dHBzOi8vd3d3LnRhbmRmb25saW5lLmNvbS9kb2kvYWJzLzEwLjEwODAvMDE2MjE0NTkuMjAxOC4xNDQ4ODI1P2pvdXJuYWxDb2RlPXVhc2EyMCkgbW9zdHJhbSBxdWUgYSBtYXRyaXogJFx0ZXh0YmZ7R30kIHF1ZSBtaW5pbWl6YSBvIHRyYcOnbyBkZSAkXHRleHRiZntWfV9oJCBzdWplaXRhIMOgIHJlc3RyacOnw6NvIGRlIHF1ZSAkXHRleHRiZntTR1N9ID0gXHRleHRiZntTfSQgw6kgZGFkYSBwb3IgJCRcdGV4dGJme0d9ID0gKFx0ZXh0YmZ7U30nIFx0ZXh0YmZ7V31fe2h9XnstMX1cdGV4dGJme1N9KV57LTF9XHRleHRiZntTfSdcdGV4dGJme1d9X2heey0xfS4kJAoKQXNzaW0sIGEgcHJldmlzw6NvIMOzdGltYSDDqSBkYWRvIHBvciAkJFx0aWxkZXtcdGV4dGJme3l9fV97dCtofSA9IFx0ZXh0YmZ7U0d9XGhhdHtcdGV4dGJme3l9fV97dCtofSA9IFx0ZXh0YmZ7U30oXHRleHRiZntTfScgXHRleHRiZntXfV97aH1eey0xfVx0ZXh0YmZ7U30pXnstMX1cdGV4dGJme1N9J1x0ZXh0YmZ7V31faF57LTF9XGhhdHtcdGV4dGJme3l9fV97dCtofS4kJAoKTm90ZSBxdWUgcGFyYSBwb2RlciB1dGlsaXphciBhIGbDs3JtdWxhIGFudGVyaW9yIG5hIHByw6F0aWNhLCBwcmVjaXNtb3MgY29uaGVjZXIgJFx0ZXh0YmZ7V31fe2h9JC4gRXhpc3RlbSB2w6FyaWFzIGZvcm1hcyBkZSBmb3JuZWNlciB1bWEgYXByb3hpbWHDp8OjbyBkZXN0YSBtYXRyaXosIGVudHJlIGVsYXM6CgoxLiAkXHRleHRiZntXfV9oID0ga19oIFx0ZXh0YmZ7SX0kIGNvbSAka19oID4gMCQuIE5vdGUgcXVlLCBmYXplbmRvICRcdGV4dGJme1h9ID0gXHRleHRiZntTfSQgZSAkXHRleHRiZnt5fSA9IFxoYXR7XHRleHRiZnt5fX0kIHRlbW9zIG8gZXN0aW1hZG9yIE1RTyBjbMOhc3NpY28gZG8gbW9kZWxvIGRlIHJlZ3Jlc3PDo28uIGBtZXRob2QgPSAib2xzImAuCjIuICRcdGV4dGJme1d9X2ggPSBrX2ggZGlhZyhcd2lkZWhhdHtcdGV4dGJme1d9fV8xKSQgY29tICRrX2ggPiAwJCBlICQkXHdpZGVoYXR7XHRleHRiZntXfX1fMSA9IFxkZnJhY3sxfXtUfVxkaXNwbGF5c3R5bGUgXHN1bV97dCA9IDF9XlQgXHRleHRiZntlfV90IFx0ZXh0YmZ7ZX1fdCcsJCQgZW0gcXVlICRcdGV4dGJme2V9X3QkIMOpIHVtIHZldG9yIGRlIHJlc8OtZHVvcyBkb3MgbW9kZWxvcyBxdWUgZ2VyYXJhbSBhcyBwcmV2aXPDtWVzIGJhc2UuIEVzdGEgZXNwZWNpZmljYcOnw6NvIHBvbmRlcmEgYXMgcHJldmlzw7VlcyBiYXNlIHV0aWxpemFuZG8gYSB2YXJpw6JuY2lhIGRvcyByZXPDrWR1b3MsIHNlbmRvIGNvbmhlY2lkbyBjb21vIG3DrW5pbW9zIHF1YWRyYWRvcyBwb25kZXJhZG9zIHV0aWxpemFuZG8gYSB2YXJpw6JuY2lhLiBgbWV0aG9kID0gIndsc192YXIiYC4KMy4gJFx0ZXh0YmZ7V31faCA9IGtfaCBcTGFtYmRhJCBjb20gJGtfaCA+IDAkLCAkXExhbWJkYSA9IGRpYWcoXHRleHRiZntTMX0pJCBlICRcdGV4dGJmezF9JCDDqSB1bSB2ZXRvciBkZSB1bnMgZGUgdGFtYW5obyAkbSQgKG7Dum1lcm8gZGUgc8OpcmllcyBubyBuw612ZWwgbWFpcyBiYWl4bykuIE5vdGUgcXVlICRcTGFtYmRhJCBjb250w6ltIG8gbsO6bWVybyBkZSB2YXJpw6JuY2lhcyBkbyBlcnJvIGNvbnRyaWJ1aW5kbyBwYXJhIGNhZGEgbsOzLiBBc3NpbSwgZXN0ZSBlc3RpbWFkb3IgZGVwZW5kZSBkYSBlc3RydXR1cmEgZGUgYWdyZWdhw6fDo28gZSBuw6NvIGRvcyBkYWRvcywgc2VuZG8gY2hhbWFkbyBkZSBfc3RydWN0dXJhbCBzY2FsaW5nXy4gYG1ldGhvZCA9ICJ3bHNfc3RydWN0ImAuCjQuICRcdGV4dGJme1d9X2ggPSBrX2ggXHRleHRiZntXfV8xJCBjb20gJGtfaCA+IDAkLiBBIHVuaWNhIHN1cG9zacOnw6NvIGRlc3RlIGVzdGltYWRvciDDqSBxdWUgbyBlcnJvIGRhIG1hdHJpeiBkZSBjb3ZhacOibmNpYSDDqSBwcm9wb3JjaW9uYWwgdW0gZG8gb3V0cm8uIFBvZGVtb3MgdXRpbGl6YXIgYSBtYXJpeiBkZSBjb3ZhcmnDom5jaWEgYW1vc3RyYWwgKGBtZXRob2QgPSAibWludF9jb3YiYCkgb3UsIHF1YW5kbyAkbSQgZm9yIGdyYW5kZSwgZXN0aW1hZG9yZXMgX3Nocmlua2FnZV8gKGBtZXRob2QgPSAibWludF9zaHJpbmsiYCkuCgoKCiMjIEV4ZW1wbG8KCgpgYGB7cn0KdG91cmlzbV9mdWxsIDwtIHRvdXJpc20gfD4KICBhZ2dyZWdhdGVfa2V5KChTdGF0ZS9SZWdpb24pICogUHVycG9zZSwgVHJpcHMgPSBzdW0oVHJpcHMpKQpgYGAKCgpgYGB7cn0KZml0IDwtIHRvdXJpc21fZnVsbCB8PgogIGZpbHRlcih5ZWFyKFF1YXJ0ZXIpIDw9IDIwMTUpIHw+CiAgbW9kZWwoYmFzZSA9IEVUUyhUcmlwcykpIHw+CiAgcmVjb25jaWxlKAogICAgYnUgPSBib3R0b21fdXAoYmFzZSksCiAgICBvbHMgPSBtaW5fdHJhY2UoYmFzZSwgbWV0aG9kID0gIm9scyIpLAogICAgdmFyID0gbWluX3RyYWNlKGJhc2UsIG1ldGhvZCA9ICJ3bHNfdmFyIiksCiAgICBzdHJ1ID0gbWluX3RyYWNlKGJhc2UsIG1ldGhvZCA9ICJ3bHNfc3RydWN0IiksCiAgICBtaW50X3NociA9IG1pbl90cmFjZShiYXNlLCBtZXRob2QgPSAibWludF9zaHJpbmsiKQogICkKYGBgCgoKYGBge3J9CmZvcmUgPC0gZml0IHw+IGZvcmVjYXN0KGggPSA4KQpgYGAKCgpgYGB7cn0KZm9yZSB8PgogIGZpbHRlcihpc19hZ2dyZWdhdGVkKFJlZ2lvbiksIGlzX2FnZ3JlZ2F0ZWQoUHVycG9zZSkpIHw+CiAgYXV0b3Bsb3QoCiAgICB0b3VyaXNtX2Z1bGwgfD4gZmlsdGVyKHllYXIoUXVhcnRlcikgPj0gMjAxMCksCiAgICBsZXZlbCA9IE5VTEwpICsKICBmYWNldF93cmFwKHZhcnMoU3RhdGUpLCBzY2FsZXMgPSAiZnJlZV95IikKYGBgCgoKYGBge3J9CmZvcmUgfD4KICBmaWx0ZXIoaXNfYWdncmVnYXRlZChTdGF0ZSksICFpc19hZ2dyZWdhdGVkKFB1cnBvc2UpKSB8PgogIGF1dG9wbG90KAogICAgdG91cmlzbV9mdWxsIHw+IGZpbHRlcih5ZWFyKFF1YXJ0ZXIpID49IDIwMTApLAogICAgbGV2ZWwgPSBOVUxMKSArCiAgZmFjZXRfd3JhcCh2YXJzKFB1cnBvc2UpLCBzY2FsZXMgPSAiZnJlZV95IikKYGBgCgpgYGB7cn0KZm9yZSB8PgogIGZpbHRlcihpc19hZ2dyZWdhdGVkKFN0YXRlKSwgaXNfYWdncmVnYXRlZChQdXJwb3NlKSkgfD4KICBhY2N1cmFjeShkYXRhID0gdG91cmlzbV9mdWxsLAogICAgICAgICAgIG1lYXN1cmVzID0gbGlzdChybXNlID0gUk1TRSwgbWFzZSA9IE1BU0UpKSB8PgogIGdyb3VwX2J5KC5tb2RlbCkgfD4KICBzdW1tYXJpc2Uocm1zZSA9IG1lYW4ocm1zZSksIG1hc2UgPSBtZWFuKG1hc2UpKQpgYGAKCmBgYHtyfQpmb3JlIHw+CiAgZmlsdGVyKGlzX2FnZ3JlZ2F0ZWQoU3RhdGUpLCAhaXNfYWdncmVnYXRlZChQdXJwb3NlKSkgfD4KICBhY2N1cmFjeShkYXRhID0gdG91cmlzbV9mdWxsLAogICAgICAgICAgIG1lYXN1cmVzID0gbGlzdChybXNlID0gUk1TRSwgbWFzZSA9IE1BU0UpKSB8PgogIGdyb3VwX2J5KC5tb2RlbCkgfD4KICBzdW1tYXJpc2Uocm1zZSA9IG1lYW4ocm1zZSksIG1hc2UgPSBtZWFuKG1hc2UpKQpgYGAKCgo=