Intro

Data collected measuring the felled sample trees has been registered in spreadsheets that replicate tally forms. The procedure transferd the measurements to a relational dababase. Data are read from the sheets, completed, verified and reshaped into tables corresponding to DB tables. The (empty) DB is generated and then populated.

Schema of the DataBase

The schema of the DB has been designed using DBSchema. The program produces a graphical representation of the schema and the SQL script file required to initialize the DB (create_DB_SQL) 2018Arzana_FustiCampione DB schema

Access to GoogleSheet

Data tally procedures and forms are presented in this document: PROTOCOLLO DI RILIEVO CANALI ESCA_V2.pdf Data have been acquired through GoogleSheet tables that reproduced forms used in the forest.

library(plyr)
library(dplyr)
library(tidyverse)
library(magrittr)
library(googlesheets)
# getwd()
source("ReadDataFromGSheet.R")
Worksheets feed constructed with public visibility
Accessing worksheet titled 'Intestazione'.

Downloading: 1.2 kB     
Downloading: 1.2 kB     
Downloading: 1.7 kB     
Downloading: 1.7 kB     
Downloading: 1.7 kB     
Downloading: 1.7 kB     
Downloading: 1.7 kB     
Downloading: 1.7 kB     
Downloading: 1.7 kB     
Downloading: 1.7 kB     
Parsed with column specification:
cols(
  complesso = col_character(),
  data = col_character(),
  rilevatori = col_character(),
  id_gradone = col_character(),
  progr_gradone = col_character(),
  id_fusto_campione = col_double(),
  specie = col_character(),
  d_130 = col_double(),
  h_ipso = col_double(),
  lung_atterrato = col_double(),
  peso_ramaglia = col_double(),
  fascine = col_character()
)
Accessing worksheet titled 'Scheda_2'.

Downloading: 1.1 kB     
Downloading: 1.1 kB     
Downloading: 1.6 kB     
Downloading: 1.6 kB     
Downloading: 1.6 kB     
Downloading: 1.6 kB     
Downloading: 1.6 kB     
Downloading: 1.6 kB     
Downloading: 1.6 kB     
Downloading: 1.6 kB     
Parsed with column specification:
cols(
  id_fusto_campione = col_double(),
  progressivo_foglio = col_double(),
  id_asta_secondaria = col_character(),
  dist_da_biforcazione = col_double(),
  diam_sezione = col_double(),
  id_asta_liv_inf = col_character()
)
Accessing worksheet titled 'Rotelle'.

Downloading: 610 B     
Downloading: 610 B     
Downloading: 620 B     
Downloading: 620 B     
Downloading: 620 B     
Downloading: 620 B     
Downloading: 620 B     
Downloading: 620 B     
Parsed with column specification:
cols(
  id_fusto_campione = col_double(),
  peso_rotelle = col_double(),
  dist_rot_id0 = col_double(),
  dist_rot_id1 = col_double(),
  dist_rot_id2 = col_double(),
  dist_rot_id3 = col_double(),
  dist_rot_id4 = col_double(),
  dist_rot_id5 = col_double(),
  dist_rot_id6 = col_double()
)
Accessing worksheet titled 'Scheda_1'.

Downloading: 1.3 kB     
Downloading: 1.3 kB     
Downloading: 2.7 kB     
Downloading: 2.7 kB     
Downloading: 4 kB     
Downloading: 4 kB     
Downloading: 4.1 kB     
Downloading: 4.1 kB     
Downloading: 5.4 kB     
Downloading: 5.4 kB     
Downloading: 5.9 kB     
Downloading: 5.9 kB     
Downloading: 7.2 kB     
Downloading: 7.2 kB     
Downloading: 8.5 kB     
Downloading: 8.5 kB     
Downloading: 9.2 kB     
Downloading: 9.2 kB     
Downloading: 10 kB     
Downloading: 10 kB     
Downloading: 12 kB     
Downloading: 12 kB     
Downloading: 12 kB     
Downloading: 12 kB     
Downloading: 14 kB     
Downloading: 14 kB     
Downloading: 15 kB     
Downloading: 15 kB     
Downloading: 15 kB     
Downloading: 15 kB     
Downloading: 17 kB     
Downloading: 17 kB     
Downloading: 18 kB     
Downloading: 18 kB     
Downloading: 19 kB     
Downloading: 19 kB     
Downloading: 20 kB     
Downloading: 20 kB     
Downloading: 21 kB     
Downloading: 21 kB     
Downloading: 23 kB     
Downloading: 23 kB     
Downloading: 23 kB     
Downloading: 23 kB     
Downloading: 24 kB     
Downloading: 24 kB     
Downloading: 26 kB     
Downloading: 26 kB     
Downloading: 27 kB     
Downloading: 27 kB     
Downloading: 27 kB     
Downloading: 27 kB     
Downloading: 27 kB     
Downloading: 27 kB     
Downloading: 27 kB     
Downloading: 27 kB     
Downloading: 27 kB     
Downloading: 27 kB     [1] READ **** File name: FustiCampione_Arzana2018_InserimentoDati - updated: 2019-01-28 10:41:46

Completing missing data

# Completing missing date
## Data mancante in scheda attribuita per logica
Intestazione <- Intestazione %>%
  mutate(data = replace(data, id_fusto_campione == 57, "25/07/18"))
# Linear interpolation of data missing in diam_sez in Scheda_2
## Interpolation function
linterp <- function(h, h1, d1, h2, d2) d1 - (h-h1)*(d1-d2)/(h2-h1)
## Sort Scheda_2 for linear interpolation of NA in diam_sezione
Scheda_2 <- arrange(Scheda_2, id_fusto_campione, id_asta_secondaria, dist_da_biforcazione)
## Interpolation
Scheda_2 <- Scheda_2 %>%
  mutate(d1 =  lag(diam_sezione),
         d2 = lead(diam_sezione),
         h1 =  lag(dist_da_biforcazione),
         h2 = lead(dist_da_biforcazione),
         diam_sez_interp = linterp(dist_da_biforcazione, h1, d1, h2, d2),
         diam_sezione = 
           ifelse(is.na(diam_sezione), diam_sez_interp, diam_sezione)) %>%
  select(-d1, -d2, -h1, -h2, -diam_sez_interp)

Prepare DB tables

# Table "Campagne"
Campagne <- tribble(
  ~ID_Campagna, ~ComplessoForestale, ~SitoDiOsservazione,
  1,  "Perdas-Monte Idolo", "Canali Esca")
# Table "Rilievi"
Rilievi <- Intestazione %>%
  group_by(data) %>%
  summarise() %>%
  ungroup() %>%
  mutate(dataRilievo = as.Date(data, format="%d/%m/%y"))
## In order to count the number of "Rilievi" piping interrupted
Rilievi <- Rilievi %>%
  mutate(ID_Campagna = Campagne[1,]$ID_Campagna, ID_Rilievo = paste0(Campagne$ID_Campagna, "_", 1:nrow(Rilievi))) %>%
  select(ID_Rilievo, ID_Campagna, dataRilievo)
# Table "Rilevatori"
Rilevatori <- tribble(
  ~ID_Rilevatore, ~Cognome, ~Nome, ~Affiliazione,
  "MRUMTT", "Mura", "Matteo",  "NFS",
  "PCCMTT", "Piccolo", "Matteo", "NFS",
  "LAIPRI", "Lai", "Piero", "Forestas",                      
  "MLSTTV", "Melis", "Ottavio", "Forestas",
  "MCLMRA", "Muceli", "Mauro", "NFS",
  "BBICRS", "Ibba", "Cristian", "CFVA",
  "MRNMRA", "Marongiu", "Mauro", "Forestas")
# Table "Squadre"
Squadre <- Intestazione %>%
  select(data, rilevatori) %>%
  mutate(data = as.Date(data, format="%d/%m/%y")) %>%
  unique %>% 
  separate(rilevatori, c("Rilevatore1", "Rilevatore2", "Rilevatore3", "Rilevatore4", "Rilevatore5", "Rilevatore6"),
           remove = T, sep = ", ", fill = "right") %>%
  gather(starts_with("Rilevatore"), key = "id_ril", value = "Cognome") %>%
  select(-id_ril) %>%
  mutate(Cognome = replace(Cognome, Cognome == "Mereu", NA)) %>%
  filter(!is.na(Cognome)) %>%
  unique %>%
  right_join(select(Rilevatori, ID_Rilevatore, Cognome),.) %>%
  select(-Cognome) %>%
  right_join(select(Rilievi, ID_Rilievo, dataRilievo), .,
             by = c("dataRilievo" = "data")) %>%
  select(-dataRilievo)
Joining, by = "Cognome"
# Table "Specie" - ID_Specie from EPPO (https://data.eppo.int/)
Specie <- tribble(
  ~ID_Specie, ~nomeScientifico, ~nomeItaliano, ~species,
  "PIURA", "Pinus radiata, D.Don", "pino di Monterey", "Monterey pine",
  "PIUPL", "Pinus pinaster, Aiton", "pino marittimo", "maritime pine")
# Table "FustiCampioneEtAdF"
FustiCampioneEtAdF <- Intestazione %>%
  full_join(select(Rotelle_gs, id_fusto_campione, peso_rotelle)) %>%
  rename(
    ID_fustoCampione = id_fusto_campione,
    ID_gradone = id_gradone,
    Prog_gradone = progr_gradone,   
    pesoFrescoRamaglia = peso_ramaglia,
    pesoRotelle = peso_rotelle,
    lunghezzaAbbattuto = lung_atterrato
      ) %>%
  mutate(dataRilievo = as.Date(data, format="%d/%m/%y")) %>%
  full_join(select(Rilievi, dataRilievo, ID_Rilievo)) %>%
  add_column(KeyID = paste0(.$ID_Rilievo, "_", .$ID_fustoCampione), .before = 1) %>%
  select(-c(complesso, data, rilevatori, fascine)) %>%
  mutate(ID_specie = map_chr(specie, ~switch(., R = "PIURA", M = "PIUPL", -1))) %>%
  select(-c(specie, dataRilievo)) %>%
  select(KeyID, ID_Rilievo, ID_fustoCampione, ID_gradone, Prog_gradone,
         ID_specie, d_130, h_ipso, lunghezzaAbbattuto, pesoFrescoRamaglia, pesoRotelle)
Joining, by = "id_fusto_campione"
Joining, by = "dataRilievo"
# Table "ProfiliFustiPrincipali"
ProfiliFustiPrincipali <- Scheda_1 %>%
  filter(!is.na(diam_sezione)) %>%
  rename(distSuolo = distanza_suolo, d_sez = diam_sezione) %>%
  right_join(select(FustiCampioneEtAdF, KeyID, ID_fustoCampione), .,
             by = c("ID_fustoCampione" = "id_fusto_campione")) %>%
  arrange(KeyID, distSuolo) %>%
  ddply(.(KeyID), mutate, ID_sez = seq_along(KeyID)) %>% # Da verificare se usare il mutate
  select(KeyID, distSuolo, d_sez) %>%
  distinct(KeyID, distSuolo, .keep_all = T)
# Table "ProfiliFustiSecondari"
ProfiliFustiSecondari <- Scheda_2 %>%
  select(id_fusto_campione, progressivo_foglio, id_asta_secondaria, dist_da_biforcazione, diam_sezione) %>%
  right_join(select(FustiCampioneEtAdF, KeyID, ID_fustoCampione), .,
             by = c("ID_fustoCampione" = "id_fusto_campione")) %>%
  select(KeyID, id_asta_secondaria, dist_da_biforcazione, diam_sezione) %>%
  rename(ID_asta = id_asta_secondaria,
         distanzaBiforcazione = dist_da_biforcazione,
         diametro = diam_sezione)
# Table "Palchi"
Palchi <- filter(Scheda_1, is.na(diam_sezione)) %>%
  select(stato_palco, numero_rami, diam_ramo_grosso, distanza_suolo, id_fusto_campione, id_asta_secondaria) %>%
  rename(stato = stato_palco,
         numeroRami = numero_rami,
         distSuolo = distanza_suolo,
         diamRamoGrosso = diam_ramo_grosso,
         ID_fustoCampione = id_fusto_campione,
         ID_asta = id_asta_secondaria) %>%
  inner_join(select(FustiCampioneEtAdF, ID_fustoCampione, KeyID),.) %>%
  ddply(.(KeyID), mutate, ID_palco = seq_along(KeyID)) %>% # Da verificare se usare il mutate
  select(-ID_fustoCampione)
Joining, by = "ID_fustoCampione"
# Table "Rotelle"
Rotelle <- Rotelle_gs %>%
  select(-peso_rotelle) %>%
  inner_join(select(FustiCampioneEtAdF, KeyID, ID_fustoCampione),., by = c(ID_fustoCampione = "id_fusto_campione"))
if (nrow(Rotelle) != nrow(Rotelle_gs)) stop("si perdono rotelle")
Rotelle <- Rotelle %>%
  select(-ID_fustoCampione) %>%
  gather(starts_with("dist_rot_id"), key = "rotella_num", value = "distSuolo") %>%
  mutate(cls_h = str_sub(rotella_num, 12)) %>%
  select(-rotella_num) %>%
  arrange(KeyID, distSuolo)
Rotelle <- Rotelle %>%
  select(KeyID, cls_h, distSuolo)
# Table "ClassiAltezza"
ClassiAltezza <-  tribble(
  ~cls_h, ~classe_altezza,
  0, "base",
  1, "50cm",
  2, "dbh",
  3, "1/4h_circa",
  4, "1/2h",
  5, "2/3h",
  6, "5/6h")

Completing DB tables

## Check for trees without any indication in "stato"
Palchi %>%
  filter(is.na(stato)) %>%
  select(KeyID) %>%
  unique %>%
  nrow
[1] 57
## Selection of the first row of each tree where "stato" is NA, and set it as "X"
Palchi <- Palchi %>%
  group_by(KeyID) %>%
  mutate(stato = replace(stato, min(distSuolo) & is.na(stato), "X")) %>%
  fill(stato)
Palchi <- Palchi %>%
  group_by(KeyID) %>%
  fill(stato)
## Selection of trees without stato indication (set to NA)
noStato <- Palchi %>%
  select(KeyID) %>%
  unique() %>%
  setdiff(filter(Palchi, stato != "X") %>% select(KeyID) %>% unique) %>%
  add_column(newStato = "not_avail")
Palchi <- Palchi %>%
  full_join(noStato) %>%
  mutate(stato = replace(stato, newStato == "not_avail", NA)) %>%
  select(-newStato) %>% 
  select(KeyID, ID_palco, stato, numeroRami, diamRamoGrosso, distSuolo, ID_asta)
Joining, by = "KeyID"

Creating and populating DB in SQLite

library(DBI)
library(RSQLite)
dbFileName <- "2018Arzana_FustiCampione.sqlite"
if (file.exists(dbFileName)) cat("ERROR: SQLite file exists and has not been overwritten!\n To produce a new SQLite file delete the old one \n") else
  {
  syCommand <- paste("sqlite3", dbFileName, "<", "DB_Schema_Arzana2018.sql")
  # Create SQLite DB file using the given schema
  system(syCommand)
  
  tablesList <-  c("Specie",
                   "Campagne",
                   "Rilevatori",
                   "Rilievi",
                   "Squadre",
                   "FustiCampioneEtAdF",
                   "ProfiliFustiPrincipali",
                   "Palchi",
                   "ProfiliFustiSecondari",
                   "ClassiAltezza",
                   "Rotelle")
  
  dbAdF <- dbConnect(RSQLite::SQLite(), dbFileName)
  
  for (tab in tablesList) {
    print(paste("writing table", tab))
    RSQLite::dbWriteTable(dbAdF, tab, get(tab), append = T)
# Populate the (empty) DB tables (Order of tables has to respect external keys)
    Sys.sleep(1)
    # dbDisconnect(dbAdF) # !!! WHY DISCONNECT !!!
  }
  }
ERROR: SQLite file exists and has not been overwritten!
 To produce a new SQLite file delete the old one 
  
LS0tCnRpdGxlOiAiMjAxOCwgQXJ6YW5hIC0gUGludXMgcGxhbnRhdGlvbiAtIHNhbXBsZSBzdGVtcyBzdXJ2ZXkiCnN1YnRpdGxlOiAiVHJhbnNmZXIgdGFsbHkgZGF0YSB0byBSREJNUyIKYXV0aG9yOgotIG5hbWU6IE1hdHRlbyBNdXJhCiAgYWZmaWxpYXRpb246IE51b3JvRm9yZXN0cnlTY2hvb2wKYWJzdHJhY3Q6IHwKICBEQiBzY2hlbWEgc3RydWN0dXJlIGlzIHByZXBhcmVkIGluIERCU2NoZW1hIGFuZCBzYXZlZCBhcyBpbWFnZSBhbmQgYXMgU1FMIHNjcmlwdC4gVGhlIHByb2NlZHVyZSByZWFkcyBpbnB1dCBkYXRhIGZyb20gR29vZ2xlU2hlZXRzLCBjb21wbGV0ZXMgdGhlIGluZm9ybWF0aW9uLCBwcm9kdWNlcyB0YWJsZXMgcmVhZHkgZm9yIHRoZSB2YWxpZGF0aW5nIERCLCByZWFkcyB0aGUgU1FMIHNjcmlwdCBjcmVhdGluZyB0aGUgU1FMaXRlIERCIGFuZCBhcHBlbmRzIHRoZSB0YWJsZXMuICAKICBSZXBvOiBodHRwczovL2dpdGxhYi5jb20vTnVvcm9Gb3Jlc3RyeVNjaG9vbC9Gb3Jlc3QtTWFuYWdlbWVudC1Ub29sc19waW5hc3Rlci1hbmQtcmFkaWF0YS1waW5lX0FyemFuYS5naXQgIAprZXl3b3JkczogInVwbG9hZGluZyBTUUxpdGUgREIiCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVCICVkLCAlWScpYCIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICBkZl9wcmludDogcGFnZWQKICBodG1sX25vdGVib29rOiBkZWZhdWx0CiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0CiAgd29yZF9kb2N1bWVudDogZGVmYXVsdAotLS0KIyBJbnRybwpEYXRhIGNvbGxlY3RlZCBtZWFzdXJpbmcgdGhlIGZlbGxlZCBzYW1wbGUgdHJlZXMgaGFzIGJlZW4gcmVnaXN0ZXJlZCBpbiBzcHJlYWRzaGVldHMgdGhhdCByZXBsaWNhdGUgdGFsbHkgZm9ybXMuIFRoZSBwcm9jZWR1cmUgdHJhbnNmZXJkIHRoZSBtZWFzdXJlbWVudHMgdG8gYSByZWxhdGlvbmFsIGRhYmFiYXNlLgpEYXRhIGFyZSByZWFkIGZyb20gdGhlIHNoZWV0cywgY29tcGxldGVkLCB2ZXJpZmllZCBhbmQgcmVzaGFwZWQgaW50byB0YWJsZXMgY29ycmVzcG9uZGluZyB0byBEQiB0YWJsZXMuIFRoZSAoZW1wdHkpIERCIGlzIGdlbmVyYXRlZCBhbmQgdGhlbiBwb3B1bGF0ZWQuCgojIFNjaGVtYSBvZiB0aGUgRGF0YUJhc2UKVGhlIHNjaGVtYSBvZiB0aGUgREIgaGFzIGJlZW4gZGVzaWduZWQgdXNpbmcgX19EQlNjaGVtYV9fLiBUaGUgcHJvZ3JhbSBwcm9kdWNlcyBhIGdyYXBoaWNhbCByZXByZXNlbnRhdGlvbiBvZiB0aGUgc2NoZW1hIGFuZCB0aGUgU1FMIHNjcmlwdCBmaWxlIHJlcXVpcmVkIHRvIGluaXRpYWxpemUgdGhlIERCIChjcmVhdGVfREJfU1FMKSAKIVsyMDE4QXJ6YW5hX0Z1c3RpQ2FtcGlvbmUgREIgc2NoZW1hXShEQl9TY2hlbWFfQXJ6YW5hMjAxOC5qcGcpIAoKIyBBY2Nlc3MgdG8gR29vZ2xlU2hlZXQKRGF0YSB0YWxseSBwcm9jZWR1cmVzIGFuZCBmb3JtcyBhcmUgcHJlc2VudGVkIGluIHRoaXMgZG9jdW1lbnQ6CltQUk9UT0NPTExPIERJIFJJTElFVk8gQ0FOQUxJIEVTQ0FfVjIucGRmXShodHRwczovL2RyaXZlLmdvb2dsZS5jb20vZHJpdmUvZm9sZGVycy8wQnc0My1rZURmSmZGV2xWRVREbERXbXRWZUVrKQpEYXRhIGhhdmUgYmVlbiBhY3F1aXJlZCB0aHJvdWdoIEdvb2dsZVNoZWV0IHRhYmxlcyB0aGF0IHJlcHJvZHVjZWQgZm9ybXMgdXNlZCBpbiB0aGUgZm9yZXN0LgoKYGBge3IgQWNjZXNzR1N9CmxpYnJhcnkocGx5cikKbGlicmFyeShkcGx5cikKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkobWFncml0dHIpCmxpYnJhcnkoZ29vZ2xlc2hlZXRzKQojIGdldHdkKCkKc291cmNlKCJSZWFkRGF0YUZyb21HU2hlZXQuUiIpCmBgYAoKIyBDb21wbGV0aW5nIG1pc3NpbmcgZGF0YQpgYGB7ciBGaWxsIGRpYW1fc2V6fQojIENvbXBsZXRpbmcgbWlzc2luZyBkYXRlCiMjIERhdGEgbWFuY2FudGUgaW4gc2NoZWRhIGF0dHJpYnVpdGEgcGVyIGxvZ2ljYQpJbnRlc3RhemlvbmUgPC0gSW50ZXN0YXppb25lICU+JQogIG11dGF0ZShkYXRhID0gcmVwbGFjZShkYXRhLCBpZF9mdXN0b19jYW1waW9uZSA9PSA1NywgIjI1LzA3LzE4IikpCgojIExpbmVhciBpbnRlcnBvbGF0aW9uIG9mIGRhdGEgbWlzc2luZyBpbiBkaWFtX3NleiBpbiBTY2hlZGFfMgoKIyMgSW50ZXJwb2xhdGlvbiBmdW5jdGlvbgpsaW50ZXJwIDwtIGZ1bmN0aW9uKGgsIGgxLCBkMSwgaDIsIGQyKSBkMSAtIChoLWgxKSooZDEtZDIpLyhoMi1oMSkKCiMjIFNvcnQgU2NoZWRhXzIgZm9yIGxpbmVhciBpbnRlcnBvbGF0aW9uIG9mIE5BIGluIGRpYW1fc2V6aW9uZQpTY2hlZGFfMiA8LSBhcnJhbmdlKFNjaGVkYV8yLCBpZF9mdXN0b19jYW1waW9uZSwgaWRfYXN0YV9zZWNvbmRhcmlhLCBkaXN0X2RhX2JpZm9yY2F6aW9uZSkKCiMjIEludGVycG9sYXRpb24KU2NoZWRhXzIgPC0gU2NoZWRhXzIgJT4lCiAgbXV0YXRlKGQxID0gIGxhZyhkaWFtX3NlemlvbmUpLAogICAgICAgICBkMiA9IGxlYWQoZGlhbV9zZXppb25lKSwKICAgICAgICAgaDEgPSAgbGFnKGRpc3RfZGFfYmlmb3JjYXppb25lKSwKICAgICAgICAgaDIgPSBsZWFkKGRpc3RfZGFfYmlmb3JjYXppb25lKSwKICAgICAgICAgZGlhbV9zZXpfaW50ZXJwID0gbGludGVycChkaXN0X2RhX2JpZm9yY2F6aW9uZSwgaDEsIGQxLCBoMiwgZDIpLAogICAgICAgICBkaWFtX3NlemlvbmUgPSAKICAgICAgICAgICBpZmVsc2UoaXMubmEoZGlhbV9zZXppb25lKSwgZGlhbV9zZXpfaW50ZXJwLCBkaWFtX3NlemlvbmUpKSAlPiUKICBzZWxlY3QoLWQxLCAtZDIsIC1oMSwgLWgyLCAtZGlhbV9zZXpfaW50ZXJwKQpgYGAKCiMgUHJlcGFyZSBEQiB0YWJsZXMKYGBge3IgUHJlcGFyZSBEQiB0YWJsZXN9CgojIFRhYmxlICJDYW1wYWduZSIKQ2FtcGFnbmUgPC0gdHJpYmJsZSgKICB+SURfQ2FtcGFnbmEsIH5Db21wbGVzc29Gb3Jlc3RhbGUsIH5TaXRvRGlPc3NlcnZhemlvbmUsCiAgMSwgICJQZXJkYXMtTW9udGUgSWRvbG8iLCAiQ2FuYWxpIEVzY2EiKQoKIyBUYWJsZSAiUmlsaWV2aSIKUmlsaWV2aSA8LSBJbnRlc3RhemlvbmUgJT4lCiAgZ3JvdXBfYnkoZGF0YSkgJT4lCiAgc3VtbWFyaXNlKCkgJT4lCiAgdW5ncm91cCgpICU+JQogIG11dGF0ZShkYXRhUmlsaWV2byA9IGFzLkRhdGUoZGF0YSwgZm9ybWF0PSIlZC8lbS8leSIpKQojIyBJbiBvcmRlciB0byBjb3VudCB0aGUgbnVtYmVyIG9mICJSaWxpZXZpIiBwaXBpbmcgaW50ZXJydXB0ZWQKUmlsaWV2aSA8LSBSaWxpZXZpICU+JQogIG11dGF0ZShJRF9DYW1wYWduYSA9IENhbXBhZ25lWzEsXSRJRF9DYW1wYWduYSwgSURfUmlsaWV2byA9IHBhc3RlMChDYW1wYWduZSRJRF9DYW1wYWduYSwgIl8iLCAxOm5yb3coUmlsaWV2aSkpKSAlPiUKICBzZWxlY3QoSURfUmlsaWV2bywgSURfQ2FtcGFnbmEsIGRhdGFSaWxpZXZvKQoKIyBUYWJsZSAiUmlsZXZhdG9yaSIKUmlsZXZhdG9yaSA8LSB0cmliYmxlKAogIH5JRF9SaWxldmF0b3JlLCB+Q29nbm9tZSwgfk5vbWUsIH5BZmZpbGlhemlvbmUsCiAgIk1SVU1UVCIsICJNdXJhIiwgIk1hdHRlbyIsICAiTkZTIiwKICAiUENDTVRUIiwgIlBpY2NvbG8iLCAiTWF0dGVvIiwgIk5GUyIsCiAgIkxBSVBSSSIsICJMYWkiLCAiUGllcm8iLCAiRm9yZXN0YXMiLCAgICAgICAgICAgICAgICAgICAgICAKICAiTUxTVFRWIiwgIk1lbGlzIiwgIk90dGF2aW8iLCAiRm9yZXN0YXMiLAogICJNQ0xNUkEiLCAiTXVjZWxpIiwgIk1hdXJvIiwgIk5GUyIsCiAgIkJCSUNSUyIsICJJYmJhIiwgIkNyaXN0aWFuIiwgIkNGVkEiLAogICJNUk5NUkEiLCAiTWFyb25naXUiLCAiTWF1cm8iLCAiRm9yZXN0YXMiKQoKIyBUYWJsZSAiU3F1YWRyZSIKU3F1YWRyZSA8LSBJbnRlc3RhemlvbmUgJT4lCiAgc2VsZWN0KGRhdGEsIHJpbGV2YXRvcmkpICU+JQogIG11dGF0ZShkYXRhID0gYXMuRGF0ZShkYXRhLCBmb3JtYXQ9IiVkLyVtLyV5IikpICU+JQogIHVuaXF1ZSAlPiUgCiAgc2VwYXJhdGUocmlsZXZhdG9yaSwgYygiUmlsZXZhdG9yZTEiLCAiUmlsZXZhdG9yZTIiLCAiUmlsZXZhdG9yZTMiLCAiUmlsZXZhdG9yZTQiLCAiUmlsZXZhdG9yZTUiLCAiUmlsZXZhdG9yZTYiKSwKICAgICAgICAgICByZW1vdmUgPSBULCBzZXAgPSAiLCAiLCBmaWxsID0gInJpZ2h0IikgJT4lCiAgZ2F0aGVyKHN0YXJ0c193aXRoKCJSaWxldmF0b3JlIiksIGtleSA9ICJpZF9yaWwiLCB2YWx1ZSA9ICJDb2dub21lIikgJT4lCiAgc2VsZWN0KC1pZF9yaWwpICU+JQogIG11dGF0ZShDb2dub21lID0gcmVwbGFjZShDb2dub21lLCBDb2dub21lID09ICJNZXJldSIsIE5BKSkgJT4lCiAgZmlsdGVyKCFpcy5uYShDb2dub21lKSkgJT4lCiAgdW5pcXVlICU+JQogIHJpZ2h0X2pvaW4oc2VsZWN0KFJpbGV2YXRvcmksIElEX1JpbGV2YXRvcmUsIENvZ25vbWUpLC4pICU+JQogIHNlbGVjdCgtQ29nbm9tZSkgJT4lCiAgcmlnaHRfam9pbihzZWxlY3QoUmlsaWV2aSwgSURfUmlsaWV2bywgZGF0YVJpbGlldm8pLCAuLAogICAgICAgICAgICAgYnkgPSBjKCJkYXRhUmlsaWV2byIgPSAiZGF0YSIpKSAlPiUKICBzZWxlY3QoLWRhdGFSaWxpZXZvKQoKIyBUYWJsZSAiU3BlY2llIiAtIElEX1NwZWNpZSBmcm9tIEVQUE8gKGh0dHBzOi8vZGF0YS5lcHBvLmludC8pClNwZWNpZSA8LSB0cmliYmxlKAogIH5JRF9TcGVjaWUsIH5ub21lU2NpZW50aWZpY28sIH5ub21lSXRhbGlhbm8sIH5zcGVjaWVzLAogICJQSVVSQSIsICJQaW51cyByYWRpYXRhLCBELkRvbiIsICJwaW5vIGRpIE1vbnRlcmV5IiwgIk1vbnRlcmV5IHBpbmUiLAogICJQSVVQTCIsICJQaW51cyBwaW5hc3RlciwgQWl0b24iLCAicGlubyBtYXJpdHRpbW8iLCAibWFyaXRpbWUgcGluZSIpCgojIFRhYmxlICJGdXN0aUNhbXBpb25lRXRBZEYiCkZ1c3RpQ2FtcGlvbmVFdEFkRiA8LSBJbnRlc3RhemlvbmUgJT4lCiAgZnVsbF9qb2luKHNlbGVjdChSb3RlbGxlX2dzLCBpZF9mdXN0b19jYW1waW9uZSwgcGVzb19yb3RlbGxlKSkgJT4lCiAgcmVuYW1lKAogICAgSURfZnVzdG9DYW1waW9uZSA9IGlkX2Z1c3RvX2NhbXBpb25lLAogICAgSURfZ3JhZG9uZSA9IGlkX2dyYWRvbmUsCiAgICBQcm9nX2dyYWRvbmUgPSBwcm9ncl9ncmFkb25lLCAgIAogICAgcGVzb0ZyZXNjb1JhbWFnbGlhID0gcGVzb19yYW1hZ2xpYSwKICAgIHBlc29Sb3RlbGxlID0gcGVzb19yb3RlbGxlLAogICAgbHVuZ2hlenphQWJiYXR0dXRvID0gbHVuZ19hdHRlcnJhdG8KICAgICAgKSAlPiUKICBtdXRhdGUoZGF0YVJpbGlldm8gPSBhcy5EYXRlKGRhdGEsIGZvcm1hdD0iJWQvJW0vJXkiKSkgJT4lCiAgZnVsbF9qb2luKHNlbGVjdChSaWxpZXZpLCBkYXRhUmlsaWV2bywgSURfUmlsaWV2bykpICU+JQogIGFkZF9jb2x1bW4oS2V5SUQgPSBwYXN0ZTAoLiRJRF9SaWxpZXZvLCAiXyIsIC4kSURfZnVzdG9DYW1waW9uZSksIC5iZWZvcmUgPSAxKSAlPiUKICBzZWxlY3QoLWMoY29tcGxlc3NvLCBkYXRhLCByaWxldmF0b3JpLCBmYXNjaW5lKSkgJT4lCiAgbXV0YXRlKElEX3NwZWNpZSA9IG1hcF9jaHIoc3BlY2llLCB+c3dpdGNoKC4sIFIgPSAiUElVUkEiLCBNID0gIlBJVVBMIiwgLTEpKSkgJT4lCiAgc2VsZWN0KC1jKHNwZWNpZSwgZGF0YVJpbGlldm8pKSAlPiUKICBzZWxlY3QoS2V5SUQsIElEX1JpbGlldm8sIElEX2Z1c3RvQ2FtcGlvbmUsIElEX2dyYWRvbmUsIFByb2dfZ3JhZG9uZSwKICAgICAgICAgSURfc3BlY2llLCBkXzEzMCwgaF9pcHNvLCBsdW5naGV6emFBYmJhdHR1dG8sIHBlc29GcmVzY29SYW1hZ2xpYSwgcGVzb1JvdGVsbGUpCgojIFRhYmxlICJQcm9maWxpRnVzdGlQcmluY2lwYWxpIgpQcm9maWxpRnVzdGlQcmluY2lwYWxpIDwtIFNjaGVkYV8xICU+JQogIGZpbHRlcighaXMubmEoZGlhbV9zZXppb25lKSkgJT4lCiAgcmVuYW1lKGRpc3RTdW9sbyA9IGRpc3RhbnphX3N1b2xvLCBkX3NleiA9IGRpYW1fc2V6aW9uZSkgJT4lCiAgcmlnaHRfam9pbihzZWxlY3QoRnVzdGlDYW1waW9uZUV0QWRGLCBLZXlJRCwgSURfZnVzdG9DYW1waW9uZSksIC4sCiAgICAgICAgICAgICBieSA9IGMoIklEX2Z1c3RvQ2FtcGlvbmUiID0gImlkX2Z1c3RvX2NhbXBpb25lIikpICU+JQogIGFycmFuZ2UoS2V5SUQsIGRpc3RTdW9sbykgJT4lCiAgZGRwbHkoLihLZXlJRCksIG11dGF0ZSwgSURfc2V6ID0gc2VxX2Fsb25nKEtleUlEKSkgJT4lICMgRGEgdmVyaWZpY2FyZSBzZSB1c2FyZSBpbCBtdXRhdGUKICBzZWxlY3QoS2V5SUQsIGRpc3RTdW9sbywgZF9zZXopICU+JQogIGRpc3RpbmN0KEtleUlELCBkaXN0U3VvbG8sIC5rZWVwX2FsbCA9IFQpCgojIFRhYmxlICJQcm9maWxpRnVzdGlTZWNvbmRhcmkiClByb2ZpbGlGdXN0aVNlY29uZGFyaSA8LSBTY2hlZGFfMiAlPiUKICBzZWxlY3QoaWRfZnVzdG9fY2FtcGlvbmUsIHByb2dyZXNzaXZvX2ZvZ2xpbywgaWRfYXN0YV9zZWNvbmRhcmlhLCBkaXN0X2RhX2JpZm9yY2F6aW9uZSwgZGlhbV9zZXppb25lKSAlPiUKICByaWdodF9qb2luKHNlbGVjdChGdXN0aUNhbXBpb25lRXRBZEYsIEtleUlELCBJRF9mdXN0b0NhbXBpb25lKSwgLiwKICAgICAgICAgICAgIGJ5ID0gYygiSURfZnVzdG9DYW1waW9uZSIgPSAiaWRfZnVzdG9fY2FtcGlvbmUiKSkgJT4lCiAgc2VsZWN0KEtleUlELCBpZF9hc3RhX3NlY29uZGFyaWEsIGRpc3RfZGFfYmlmb3JjYXppb25lLCBkaWFtX3NlemlvbmUpICU+JQogIHJlbmFtZShJRF9hc3RhID0gaWRfYXN0YV9zZWNvbmRhcmlhLAogICAgICAgICBkaXN0YW56YUJpZm9yY2F6aW9uZSA9IGRpc3RfZGFfYmlmb3JjYXppb25lLAogICAgICAgICBkaWFtZXRybyA9IGRpYW1fc2V6aW9uZSkKCiMgVGFibGUgIlBhbGNoaSIKUGFsY2hpIDwtIGZpbHRlcihTY2hlZGFfMSwgaXMubmEoZGlhbV9zZXppb25lKSkgJT4lCiAgc2VsZWN0KHN0YXRvX3BhbGNvLCBudW1lcm9fcmFtaSwgZGlhbV9yYW1vX2dyb3NzbywgZGlzdGFuemFfc3VvbG8sIGlkX2Z1c3RvX2NhbXBpb25lLCBpZF9hc3RhX3NlY29uZGFyaWEpICU+JQogIHJlbmFtZShzdGF0byA9IHN0YXRvX3BhbGNvLAogICAgICAgICBudW1lcm9SYW1pID0gbnVtZXJvX3JhbWksCiAgICAgICAgIGRpc3RTdW9sbyA9IGRpc3RhbnphX3N1b2xvLAogICAgICAgICBkaWFtUmFtb0dyb3NzbyA9IGRpYW1fcmFtb19ncm9zc28sCiAgICAgICAgIElEX2Z1c3RvQ2FtcGlvbmUgPSBpZF9mdXN0b19jYW1waW9uZSwKICAgICAgICAgSURfYXN0YSA9IGlkX2FzdGFfc2Vjb25kYXJpYSkgJT4lCiAgaW5uZXJfam9pbihzZWxlY3QoRnVzdGlDYW1waW9uZUV0QWRGLCBJRF9mdXN0b0NhbXBpb25lLCBLZXlJRCksLikgJT4lCiAgZGRwbHkoLihLZXlJRCksIG11dGF0ZSwgSURfcGFsY28gPSBzZXFfYWxvbmcoS2V5SUQpKSAlPiUgIyBEYSB2ZXJpZmljYXJlIHNlIHVzYXJlIGlsIG11dGF0ZQogIHNlbGVjdCgtSURfZnVzdG9DYW1waW9uZSkKCiMgVGFibGUgIlJvdGVsbGUiClJvdGVsbGUgPC0gUm90ZWxsZV9ncyAlPiUKICBzZWxlY3QoLXBlc29fcm90ZWxsZSkgJT4lCiAgaW5uZXJfam9pbihzZWxlY3QoRnVzdGlDYW1waW9uZUV0QWRGLCBLZXlJRCwgSURfZnVzdG9DYW1waW9uZSksLiwgYnkgPSBjKElEX2Z1c3RvQ2FtcGlvbmUgPSAiaWRfZnVzdG9fY2FtcGlvbmUiKSkKCmlmIChucm93KFJvdGVsbGUpICE9IG5yb3coUm90ZWxsZV9ncykpIHN0b3AoInNpIHBlcmRvbm8gcm90ZWxsZSIpCgpSb3RlbGxlIDwtIFJvdGVsbGUgJT4lCiAgc2VsZWN0KC1JRF9mdXN0b0NhbXBpb25lKSAlPiUKICBnYXRoZXIoc3RhcnRzX3dpdGgoImRpc3Rfcm90X2lkIiksIGtleSA9ICJyb3RlbGxhX251bSIsIHZhbHVlID0gImRpc3RTdW9sbyIpICU+JQogIG11dGF0ZShjbHNfaCA9IHN0cl9zdWIocm90ZWxsYV9udW0sIDEyKSkgJT4lCiAgc2VsZWN0KC1yb3RlbGxhX251bSkgJT4lCiAgYXJyYW5nZShLZXlJRCwgZGlzdFN1b2xvKQoKUm90ZWxsZSA8LSBSb3RlbGxlICU+JQogIHNlbGVjdChLZXlJRCwgY2xzX2gsIGRpc3RTdW9sbykKCiMgVGFibGUgIkNsYXNzaUFsdGV6emEiCkNsYXNzaUFsdGV6emEgPC0gIHRyaWJibGUoCiAgfmNsc19oLCB+Y2xhc3NlX2FsdGV6emEsCiAgMCwgImJhc2UiLAogIDEsICI1MGNtIiwKICAyLCAiZGJoIiwKICAzLCAiMS80aF9jaXJjYSIsCiAgNCwgIjEvMmgiLAogIDUsICIyLzNoIiwKICA2LCAiNS82aCIpCmBgYAoKIyBDb21wbGV0aW5nIERCIHRhYmxlcwpgYGB7ciBDb21wbGV0aW5nIERCIHRhYmxlc30KIyMgQ2hlY2sgZm9yIHRyZWVzIHdpdGhvdXQgYW55IGluZGljYXRpb24gaW4gInN0YXRvIgpQYWxjaGkgJT4lCiAgZmlsdGVyKGlzLm5hKHN0YXRvKSkgJT4lCiAgc2VsZWN0KEtleUlEKSAlPiUKICB1bmlxdWUgJT4lCiAgbnJvdwoKIyMgU2VsZWN0aW9uIG9mIHRoZSBmaXJzdCByb3cgb2YgZWFjaCB0cmVlIHdoZXJlICJzdGF0byIgaXMgTkEsIGFuZCBzZXQgaXQgYXMgIlgiClBhbGNoaSA8LSBQYWxjaGkgJT4lCiAgZ3JvdXBfYnkoS2V5SUQpICU+JQogIG11dGF0ZShzdGF0byA9IHJlcGxhY2Uoc3RhdG8sIG1pbihkaXN0U3VvbG8pICYgaXMubmEoc3RhdG8pLCAiWCIpKSAlPiUKICBmaWxsKHN0YXRvKQoKUGFsY2hpIDwtIFBhbGNoaSAlPiUKICBncm91cF9ieShLZXlJRCkgJT4lCiAgZmlsbChzdGF0bykKCiMjIFNlbGVjdGlvbiBvZiB0cmVlcyB3aXRob3V0IHN0YXRvIGluZGljYXRpb24gKHNldCB0byBOQSkKbm9TdGF0byA8LSBQYWxjaGkgJT4lCiAgc2VsZWN0KEtleUlEKSAlPiUKICB1bmlxdWUoKSAlPiUKICBzZXRkaWZmKGZpbHRlcihQYWxjaGksIHN0YXRvICE9ICJYIikgJT4lIHNlbGVjdChLZXlJRCkgJT4lIHVuaXF1ZSkgJT4lCiAgYWRkX2NvbHVtbihuZXdTdGF0byA9ICJub3RfYXZhaWwiKQoKUGFsY2hpIDwtIFBhbGNoaSAlPiUKICBmdWxsX2pvaW4obm9TdGF0bykgJT4lCiAgbXV0YXRlKHN0YXRvID0gcmVwbGFjZShzdGF0bywgbmV3U3RhdG8gPT0gIm5vdF9hdmFpbCIsIE5BKSkgJT4lCiAgc2VsZWN0KC1uZXdTdGF0bykgJT4lIAogIHNlbGVjdChLZXlJRCwgSURfcGFsY28sIHN0YXRvLCBudW1lcm9SYW1pLCBkaWFtUmFtb0dyb3NzbywgZGlzdFN1b2xvLCBJRF9hc3RhKQpgYGAKCmBgYHtyLCByZXN1bHRzPSdhc2lzJywgZWNobyA9IEZBTFNFLCAgZXZhbD0oa25pdHI6Om9wdHNfa25pdCRnZXQoJ3JtYXJrZG93bi5wYW5kb2MudG8nKSA9PSAnbGF0ZXgnKX0KY2F0KCdcXHBhZ2VicmVhaycpCmBgYAoKIyBDcmVhdGluZyBhbmQgcG9wdWxhdGluZyBEQiBpbiBTUUxpdGUKYGBge3IgQ3JlYXRlIERCfQpsaWJyYXJ5KERCSSkKbGlicmFyeShSU1FMaXRlKQoKZGJGaWxlTmFtZSA8LSAiMjAxOEFyemFuYV9GdXN0aUNhbXBpb25lLnNxbGl0ZSIKCmlmIChmaWxlLmV4aXN0cyhkYkZpbGVOYW1lKSkgY2F0KCJFUlJPUjogU1FMaXRlIGZpbGUgZXhpc3RzIGFuZCBoYXMgbm90IGJlZW4gb3ZlcndyaXR0ZW4hXG4gVG8gcHJvZHVjZSBhIG5ldyBTUUxpdGUgZmlsZSBkZWxldGUgdGhlIG9sZCBvbmUgXG4iKSBlbHNlCiAgewogIHN5Q29tbWFuZCA8LSBwYXN0ZSgic3FsaXRlMyIsIGRiRmlsZU5hbWUsICI8IiwgIkRCX1NjaGVtYV9BcnphbmEyMDE4LnNxbCIpCiAgIyBDcmVhdGUgU1FMaXRlIERCIGZpbGUgdXNpbmcgdGhlIGdpdmVuIHNjaGVtYQogIHN5c3RlbShzeUNvbW1hbmQpCiAgCiAgdGFibGVzTGlzdCA8LSAgYygiU3BlY2llIiwKICAgICAgICAgICAgICAgICAgICJDYW1wYWduZSIsCiAgICAgICAgICAgICAgICAgICAiUmlsZXZhdG9yaSIsCiAgICAgICAgICAgICAgICAgICAiUmlsaWV2aSIsCiAgICAgICAgICAgICAgICAgICAiU3F1YWRyZSIsCiAgICAgICAgICAgICAgICAgICAiRnVzdGlDYW1waW9uZUV0QWRGIiwKICAgICAgICAgICAgICAgICAgICJQcm9maWxpRnVzdGlQcmluY2lwYWxpIiwKICAgICAgICAgICAgICAgICAgICJQYWxjaGkiLAogICAgICAgICAgICAgICAgICAgIlByb2ZpbGlGdXN0aVNlY29uZGFyaSIsCiAgICAgICAgICAgICAgICAgICAiQ2xhc3NpQWx0ZXp6YSIsCiAgICAgICAgICAgICAgICAgICAiUm90ZWxsZSIpCiAgCiAgZGJBZEYgPC0gZGJDb25uZWN0KFJTUUxpdGU6OlNRTGl0ZSgpLCBkYkZpbGVOYW1lKQogIAogIGZvciAodGFiIGluIHRhYmxlc0xpc3QpIHsKICAgIHByaW50KHBhc3RlKCJ3cml0aW5nIHRhYmxlIiwgdGFiKSkKICAgIFJTUUxpdGU6OmRiV3JpdGVUYWJsZShkYkFkRiwgdGFiLCBnZXQodGFiKSwgYXBwZW5kID0gVCkKIyBQb3B1bGF0ZSB0aGUgKGVtcHR5KSBEQiB0YWJsZXMgKE9yZGVyIG9mIHRhYmxlcyBoYXMgdG8gcmVzcGVjdCBleHRlcm5hbCBrZXlzKQogICAgU3lzLnNsZWVwKDEpCiAgICAjIGRiRGlzY29ubmVjdChkYkFkRikgIyAhISEgV0hZIERJU0NPTk5FQ1QgISEhCiAgfQogIH0KICAKYGBgCgpgYGB7ciBlY2hvPUZBTFNFLCBldmFsPUZBTFNFfQpmbiA8LSAiRnJvbUdTdG9EQi8yMDE4QXJ6YW5hUGludXNTdXJ2ZXlfVHJhbnNmZXJUYWxseVRvVGhlREIiCnJtYXJrZG93bjo6cmVuZGVyKHBhc3RlMChmbiwgIi5SbWQiKSkKbWFya2Rvd246OnJwdWJzVXBsb2FkKGZuLCBwYXN0ZTAoZm4sICIubmIuaHRtbCIpLCBpZCA9ICJodHRwczovL2FwaS5ycHVicy5jb20vYXBpL3YxL2RvY3VtZW50LzQ1NTQ0Mi83NmMxNTdjYzE5YTU0M2I4ODdlNmRjNzg2NWM5ZmIwYyIpCiMgJGNvbnRpbnVlVXJsCiMgWzFdICJodHRwOi8vcnB1YnMuY29tL3Njb3R0aS80NTU0NDIiCmBgYAoK