1 Settings

There are four dependencies used in this demo. The most important is the RNetCDF package. This is a low-level R interface to the file format NetCDF designed by Unidata. Other packages to interact with NetCDF in R are ncdf4 and tidync.

The rest of libraries are used as helpers.

library(RNetCDF)
library(readr)
library(dplyr)
library(glue)

2 An example data set

This is a fragment of a real data set, adjusted to serve as a small example. The names of the columns are Darwin Core terms

dataset <- read_csv("./data/raw/dataset.csv")
#> Rows: 10 Columns: 7
#> ── Column specification ────────────────────────────────────────────────────────
#> Delimiter: ","
#> chr  (2): scientificName, scientificNameID
#> dbl  (4): decimalLongitude, decimalLatitude, AphiaID, occurrenceStatus
#> date (1): eventDate
#> 
#> ℹ Use `spec()` to retrieve the full column specification for this data.
#> ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

dataset
#> # A tibble: 10 × 7
#>    decimalLongitude decimalLatitude eventDate  scientificName   scientificNameID
#>               <dbl>           <dbl> <date>     <chr>            <chr>           
#>  1            -5.70            56.2 2019-06-17 Axinella infund… urn:lsid:marine…
#>  2            -5.27            51.7 2019-06-17 Axinella infund… urn:lsid:marine…
#>  3            -5.27            51.7 2019-06-17 Axinella infund… urn:lsid:marine…
#>  4           -44.2            -24.3 2019-06-17 Pachastrella mo… urn:lsid:marine…
#>  5           -23.5             14.9 1986-08-22 Pheronema        urn:lsid:marine…
#>  6           -24.6             15.0 1986-08-24 Pheronema        urn:lsid:marine…
#>  7             9.62            67.5 2019-06-17 Thenea levis     urn:lsid:marine…
#>  8            10.3             67.9 2019-06-17 Thenea levis     urn:lsid:marine…
#>  9             9.52            67.3 2019-06-17 Thenea levis     urn:lsid:marine…
#> 10            -5.74            57.1 2019-06-17 Axinella infund… urn:lsid:marine…
#> # … with 2 more variables: AphiaID <dbl>, occurrenceStatus <dbl>

3 Arrange the data set

The variable of interest is occurrenceStatus. It has either presences (1) or absences (0)

It can be considered that this variable is defined by four dimensions: Spatially, these are decimalLongitude and decimalLatitude. Timewise is the column eventDate.

The fourth dimension that defines the variable of interest here is the taxon, which is shown in the columns scientificName, scientificNameID and AphiaID. The first is the scientific name of the taxa, while scientificNameID has the Life Science Identifier (LSID) as an uri. The column AphiaID is the unique identifier for a taxon name used in the World Record of Marine Species.

3.1 Technical constrain: Edit eventDate

The eventDate must be transformed to temporal amounts. These are seconds, days, years etc since a certain date. There is a helper in the RNetCDF package: uitinvcal.nc()

In this demo, the units will be days since 1970-01-01 00:00:00.

dataset$time <- utinvcal.nc(
  unitstring = "days since 1970-01-01 00:00:00" , 
  value = as.POSIXct(dataset$eventDate, tz = "UTC")
)

unique(dataset$eventDate)
#> [1] "2019-06-17" "1986-08-22" "1986-08-24"
unique(dataset$time)
#> [1] 18064  6077  6079

3.2 Get all possible combination of the dimensions

The key to transform a data frame to netcdf, is that the data must be transformed into a 4D array. To do that, all the possible combinations of the dimensions must be attached. This will coerce NA or empty values in the variable of interest as there will be no value for these combinations.

The base function expand.grid() allows to pass vectors and create a data frame with all the possible combinations of those vectors.

# First add an unique identifier by the combination of: 
# decimaLongitude, decimalLatitude, eventDate and AphiaID
dataset <- dataset %>%
  mutate(
    id = glue("{decimalLongitude}-{decimalLatitude}-{time}-{AphiaID}")
  )

# Extract the unique and sorted values of the 4 dimensions
lon = sort(unique(dataset$decimalLongitude))
lat = sort(unique(dataset$decimalLatitude))
time = sort(unique(dataset$time))

# Taxon will be put in a new data frame
taxon <- tibble(
  aphiaid = dataset$AphiaID,
  taxon_name = dataset$scientificName,
  taxon_lsid = dataset$scientificNameID) %>% 
  distinct() %>%
  arrange(aphiaid)

# Use expand.grid() to create a data frame with all the possible 
# combinations of the 4 dimensions
longer <- expand.grid(lon = lon, lat = lat, time = time, 
                     aphiaid = taxon$aphiaid, 
                     stringsAsFactors = FALSE)

# Define unique identifier again and merge the variable occurrenceStatus 
# with presences and absences

dataset <- dataset %>%
  select(id, occurrenceStatus)

longer <- longer %>% 
  mutate(
    id = glue("{lon}-{lat}-{time}-{aphiaid}")
  ) %>%
  left_join(dataset) %>%
  select(-id)
#> Joining, by = "id"

# Save for later
write_csv(longer, "./data/derived/longer.csv")

# Inspect
longer
#> # A tibble: 1,200 × 5
#>       lon   lat  time aphiaid occurrenceStatus
#>     <dbl> <dbl> <dbl>   <dbl>            <dbl>
#>  1 -44.2  -24.3  6077  132098               NA
#>  2 -24.6  -24.3  6077  132098               NA
#>  3 -23.5  -24.3  6077  132098               NA
#>  4  -5.74 -24.3  6077  132098               NA
#>  5  -5.70 -24.3  6077  132098               NA
#>  6  -5.27 -24.3  6077  132098               NA
#>  7  -5.27 -24.3  6077  132098               NA
#>  8   9.52 -24.3  6077  132098               NA
#>  9   9.62 -24.3  6077  132098               NA
#> 10  10.3  -24.3  6077  132098               NA
#> # … with 1,190 more rows

3.3 Turn data frame into a 4D array

NetCDF is designed to host array-oriented scientific data (ref). Transforming into an object of type array in R will allow the easiest way to add data into a NetCDF file.

In the demo dataset, we know that the total length of each dimension are:

  • lon: 10
  • lat: 10
  • time: 3
  • taxa: 4

These lengths will be passed to the R function array() as a vector: c(10, 10, 3, 4). We will also pass the variable of interest longer$occurrenceStatus. However, this variable must have as length the product of all lengths of all dimensions: This is why we got all the combinations of the dimensions via expand.grid().

# The product of the lengths of all dimensions
10 * 10 * 3 * 4
#> [1] 1200

# Is the same as the length of the variable of interest, including all 
# possible combinations of the dimensions even if this coerce NA's
length(longer$occurrenceStatus)
#> [1] 1200

# Create array
array <- array(
  data = longer$occurrenceStatus,
  dim = c(10, 10, 3, 4)
)

Note that this won’t always be possible as adding all the possible combinations makes the variable of interest grow exponentially. In R, vectors, data frames and arrays are read into memory. It is common that your machine won’t have enough memory to handle such amount of data.

But worry not: There are workarounds. These are discussed later below in this demo.

4 Transform into NetCDF

4.1 Create a placeholder

The first step is to create a netcdf file that will work as a placeholder to add the data. We use the R package RNetCDF

# Create nc file
nc <- create.nc("./data/derived/foo.nc") 

4.2 Define dimensions

Each dimensions has an homonymous variable assigned along with number of attributes. This is a technical requirement. The dimensions must have the length of the unique values of each dimension.

Some extra attributes must be added to meet the CF-Convention.

In addition, the homonymous variable defining the dimensions must have their own data. The data are passed as a 1D vector to var.put.nc(), specifying in which variable has to be written.

4.2.1 Longitude

# Define lon dimension
dim.def.nc(nc, dimname = "lon", dimlength = length(lon)) 

# Define lon variable
var.def.nc(nc, varname = "lon", vartype = "NC_DOUBLE", dimensions = "lon")

# Add attributes
att.put.nc(nc, variable = "lon", name = "units", type = "NC_CHAR", value = "degrees_east")
att.put.nc(nc, variable = "lon", name = "standard_name", type = "NC_CHAR", value = "longitude")
att.put.nc(nc, variable = "lon", name = "long_name", type = "NC_CHAR", value = "Longitude")

# Put data
var.put.nc(nc, variable = "lon", data = lon) 

# Check
var.get.nc(nc, variable = "lon")
#>  [1] -44.16500 -24.63330 -23.53330  -5.73876  -5.70009  -5.26890  -5.26569
#>  [8]   9.52050   9.61616  10.34300

4.2.2 Latitude

# Define lat dimension
dim.def.nc(nc, dimname = "lat", dimlength = length(lat)) 

# Define lat variable
var.def.nc(nc, varname = "lat", vartype = "NC_DOUBLE", dimensions = "lat")

# Add attributes
att.put.nc(nc, variable = "lat", name = "units", type = "NC_CHAR", value = "degrees_north")
att.put.nc(nc, variable = "lat", name = "standard_name", type = "NC_CHAR", value = "latitude")
att.put.nc(nc, variable = "lat", name = "long_name", type = "NC_CHAR", value = "Latitude")

# Put data
var.put.nc(nc, variable = "lat", data = lat) 

# Check
var.get.nc(nc, variable = "lat")
#>  [1] -24.34700  14.88330  14.95000  51.69739  51.70180  56.24024  57.11223
#>  [8]  67.31833  67.53900  67.85716

4.2.3 Time

# Define time dimension
dim.def.nc(nc, dimname = "time", dimlength = length(time)) 

# Define time variable
var.def.nc(nc, varname = "time", vartype = "NC_DOUBLE", dimensions = "time")

# Add attributes
att.put.nc(nc, variable = "time", name = "standard_name", type = "NC_CHAR", value = "time")
att.put.nc(nc, variable = "time", name = "long_name", type = "NC_CHAR", value = "Time")
att.put.nc(nc, variable = "time", name = "units", type = "NC_CHAR", value = "days since 1970-01-01 00:00:00")
att.put.nc(nc, variable = "time", name = "calendar", type = "NC_CHAR", value = "gregorian")

# Put data
var.put.nc(nc, variable = "time", data = time)

# Check
var.get.nc(nc, variable = "time")
#> [1]  6077  6079 18064

4.2.4 Taxon

To define the taxon dimension, there will be one dimension called aphiaid, with three variables assigned:

  • aphiaid: the actual aphiaid as an integer
  • taxon_name: the scientific name of the taxa
  • taxon_lsid: the LSID (unique identifier) of the taxa

These are technical requirements both for ERDDAP and to meet the CF-Convention.

The dimension aphiaid will be used later to define the variable of interest. It is a better practice to use the AphiaID instead of, for instance, the scientific name in taxon_name, because NetCDF works better with numeric data types than with characters.

Note that character variables in NetCDF require to be defined also by a dimension typically called “string”. Its length is the total number of characters that can be hosted in the variable of type character. This is a requirement of NetCDF4.

4.2.4.1 aphiaid

# Define the aphia and string80 dimensions
dim.def.nc(nc, dimname = "aphiaid", dimlength = nrow(taxon))
dim.def.nc(nc, dimname = "string80", dimlength = 80)

# Add aphiaid variable and attributes 
var.def.nc(nc, varname = "aphiaid", vartype = "NC_INT", dimensions = "aphiaid")
att.put.nc(nc, variable = "aphiaid", name = "long_name", type = "NC_CHAR", value = "Life Science Identifier - World Register of Marine Species")

# Put aphiaid data
var.put.nc(nc, variable = "aphiaid", data = taxon$aphiaid)

# Check
var.get.nc(nc, variable = "aphiaid")
#> [1] 132098 132480 134078 134105

4.2.4.2 taxon_name

# Add taxon_name variable and attributes
var.def.nc(nc, varname = "taxon_name", vartype = "NC_CHAR", dimension = c("string80", "aphiaid"))
att.put.nc(nc, variable = "taxon_name", name = "standard_name", type = "NC_CHAR", value = "biological_taxon_name")
att.put.nc(nc, variable = "taxon_name", name = "long_name", type = "NC_CHAR", value = "Scientific name of the taxa")

# Put taxon_name data
var.put.nc(nc, variable = "taxon_name", data = taxon$taxon_name)

# Check
var.get.nc(nc, variable = "taxon_name")
#> [1] "Pheronema"                  "Axinella infundibuliformis"
#> [3] "Pachastrella monilifera"    "Thenea levis"

4.2.4.3 taxon_lsid

# Add taxon_lsid variable and attributes
var.def.nc(nc, varname = "taxon_lsid", vartype = "NC_CHAR", dimension = c("string80", "aphiaid"))
att.put.nc(nc, variable = "taxon_lsid", name = "standard_name", type = "NC_CHAR", value = "biological_taxon_lsid")
att.put.nc(nc, variable = "taxon_lsid", name = "long_name", type = "NC_CHAR", value = "Life Science Identifier - World Register of Marine Species")

# Put taxon_name data
var.put.nc(nc, variable = "taxon_lsid", data = taxon$taxon_name)

# Check
var.get.nc(nc, variable = "taxon_lsid")
#> [1] "Pheronema"                  "Axinella infundibuliformis"
#> [3] "Pachastrella monilifera"    "Thenea levis"

4.2.5 Coordinate Reference System

A non-dimensional variable will be defined to host all the info about the Coordinate Reference System (CRS). This is useful for some GIS software.

It is assumed that the CRS is WGS84. If it was different: transform decimalLatitude and decimalLongitude to WGS84 before hand. See R package sf.

# Define non-dimensional crs variable 
var.def.nc(nc, varname = "crs", vartype = "NC_CHAR", dimensions = NA)

# Add attributes
att.put.nc(nc, variable = "crs", name = "long_name", type = "NC_CHAR", value = "Coordinate Reference System")
att.put.nc(nc, variable = "crs", name = "geographic_crs_name", type = "NC_CHAR", value = "WGS 84")
att.put.nc(nc, variable = "crs", name = "grid_mapping_name", type = "NC_CHAR", value = "latitude_longitude")
att.put.nc(nc, variable = "crs", name = "reference_ellipsoid_name", type = "NC_CHAR", value = "WGS 84")
att.put.nc(nc, variable = "crs", name = "horizontal_datum_name", type = "NC_CHAR", value = "WGS 84")
att.put.nc(nc, variable = "crs", name = "prime_meridian_name", type = "NC_CHAR", value = "Greenwich")
att.put.nc(nc, variable = "crs", name = "longitude_of_prime_meridian", type = "NC_DOUBLE", value = 0.)
att.put.nc(nc, variable = "crs", name = "semi_major_axis", type = "NC_DOUBLE", value = 6378137.)
att.put.nc(nc, variable = "crs", name = "semi_minor_axis", type = "NC_DOUBLE", value = 6356752.314245179)
att.put.nc(nc, variable = "crs", name = "inverse_flattening", type = "NC_DOUBLE", value = 298.257223563)
att.put.nc(nc, variable = "crs", name = "spatial_ref", type = "NC_CHAR", value = 'GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],AUTHORITY[\"EPSG\",\"4326\"]]')
att.put.nc(nc, variable = "crs", name = "GeoTransform", type = "NC_CHAR", value = '-180 0.08333333333333333 0 90 0 -0.08333333333333333 ')

4.3 Define the variable of interest

The variable to add the presence/absence data must be defined by the four dimensions considered in this demo. This is passed to var.def.nc() in the argument dimensions as a vector containing the names of the dimensions.

The values stating presences and absences are 1 and 0, hence the variable must be of type integer.

Some other attributes are added. E.g. _FillValue is a requirement for the CF-Convention stating what value will be used in case of NULL or NA. This is typically -99999. The attribute long_name is free text and it describes the variable.

# Create the presence_absence variable defined by the four dimensions
var.def.nc(nc, varname = "presence_absence", vartype = "NC_INT", dimensions = c("lon", "lat", "time", "aphiaid"))

# Add attributes
att.put.nc(nc, variable = "presence_absence", name = "_FillValue", type = "NC_INT", value = -99999)
att.put.nc(nc, variable = "presence_absence", name = "long_name", type = "NC_CHAR", value = "Probability of occurrence of biological entity")

If there were a standard name for such a variable in the CF-Convention, this would be add to an attribute named standard_name.

4.3.1 Add data from a 4D Array

As we previously created a 4D array, we can pass this directly to var.put.nc()

var.put.nc(nc, variable = "presence_absence", data = array) 

4.3.2 Add data from a vector: All at once

Adding the data from a data set or a vector is also possible by using the parameters start and count in var.put.nc() ** In start, each number indicates the index from which you start adding data for each variable. ** In count, each number indicates the length of the data added for each variable.

In this demo, as we have a 4D variable, the vectors will be of length 4. We can add all the data at once by setting each start index as 1, and count as the length of each dimension. Remember the length of lon is 10, lat is 10, time is 3 andtaxon is 4. start is set as c(1, 1, 1, 1) as we add all the date from the beginning of each dimension.

var.put.nc(nc, variable = "presence_absence", 
           data = longer$occurrenceStatus, 
           start = c(1, 1, 1, 1), 
           count = c(10, 10, 3, 4)) 

4.3.3 Add data from a vector: In chunks

However, out in the wild the data product will become very large when adding all possible combinations of the dimensions. See how the demo data set of only 10 rows exploded up to 1200 rows when adding all possible combinations of the four dimensions

nrow(dataset)
#> [1] 10

nrow(longer)
#> [1] 1200

Imagine what would happen with a real data set with thousands of rows!

Typically you won’t have enough memory in your computer to create such a large array. To work around this issue, you can add data in chunks by setting correctly start and count

For instance, you can loop over the data to add one taxon per iteration.

# Iterate from 1 to 4 as there are 4 possible taxons
for(i in 1:4){
  
  taxa_i <- taxon$aphiaid[i]
  
  data_at_taxa_i <- subset(longer, longer$aphiaid == taxa_i)
  
  var.put.nc(nc, variable = "presence_absence", data = data_at_taxa_i$occurrenceStatus, start = c(1, 1, 1, i), count = c(10, 10, 3, 1)) 
  
}

Note that the last element of start is set as i, which is the length of the dimension aphiaid: 1 to 4

The last element of count corresponds to the count of aphiaid. It is set as 1 because in each iteration only the data of one taxon is put in the variable.

4.3.4 Add data from a vector: One by one

Furthermore, you can nest loops as much as you want to add the slice of data that you can handle at once. Below there is an extreme example of a four times nested loop, in which in each iteration only one data is added to the NetCDF file.

Remember to use the length of each dimension in the for() loop. Note that count is set as c(1, 1, 1, 1) as only one data point is added in each iteration. The argument start contains the index of each loop.

for(loni in 1:10){
  for(lati in 1:10){
    for(timei in 1:3){
      for(taxai in 1:4){
        
        # Read only one row per iteration
        data_at_i <- longer %>%
          filter(
            aphiaid == taxon$aphiaid[taxai],
            time == time[timei],
            lat == lat[lati],
            lon == lon[loni]
          )
        
        # Put the data into 
        var.put.nc(nc, variable = "presence_absence", 
                   data = data_at_i$occurrenceStatus, 
                   start = c(loni, lati, timei, taxai), 
                   count = c(1, 1, 1, 1)) 
      }
    }
  }
}

Note this is an extreme example and its performance is not optimal.

4.3.5 Add data from disk

These loops are specially useful to turn large amounts of data into NetCDF as you can have your data saved into disk and iterate to read a certain part of it and put into netcdf at a time.

In next and last example, it is assumed that the data is too large to be read into memory. In this case, we can write a loop that reads a chunk of data that we know that R can handle, inmediately put it in the NetCDF file, and once it is saved there as NetCDF simply remove from memory.

You can read chunks of data from a .csv file either with readr::read_csv_chunked or sqldf::read.csv.sql. We will use the first in this example. Remember we saved the longer data frame into ./data/derived/longer.csv.

for(i in 1:4){
  
  # Identify the taxa of interest based on the i index
  taxa_i <- taxon$aphiaid[i]
  
  # Read a chunk of data, subsetting to only the taxa of interest in each iteration
  data_at_taxa_i <- read_csv_chunked(
    file = './data/derived/longer.csv', 
    callback = DataFrameCallback$new(
      function(x, pos) subset(x, aphiaid == taxa_i)
    )
  )
  
  # Put the data into the NetCDF file
  var.put.nc(nc, variable = "presence_absence", 
             data = data_at_taxa_i$occurrenceStatus, 
             start = c(1, 1, 1, i), 
             count = c(10, 10, 3, 1)) 
  
  # Inmediately remove the dataset read in the iteration to free up memory
  rm(data_at_taxa_i)
}

4.4 Global Attributes

NetCDF files can host metadata in the form of global attributes. These contain information such as the author, data of creation, affiliation, citation or license.

See below an example of how defining a number of global attributes and adding them programatically.

Click on code to unfold

attributes <- list(
  title = "Example title",
  summary = "This is the result of a demo to show how to turn a variable defined by four dimensions into a netcdf array.",                       
  Conventions = "CF-1.8",
  # id = "",
  naming_authority = "emodnet-biology.eu",
  history = "https://github.com/EMODnet/EMODnet-Biology-products-erddap-demo",
  source = "https://github.com/EMODnet/EMODnet-Biology-products-erddap-demo",
  # processing_level = "",
  # comment = "", 
  # acknowledgment = "",
  license = "CC-BY",
  standard_name_vocabulary = "CF Standard Name Table v1.8",
  date_created = as.character(Sys.Date()),
  creator_name = "Salvador Fernandez",
  creator_email = "salvador.fernandez@vliz.be",
  creator_url = "www.vliz.be",
  institution = "Flanders Marine Institute (VLIZ)",
  project = "EMODnet-Biology",
  publisher_name = "EMODnet-Biology",                 
  publisher_email = "bio@emodnet.eu",                
  publisher_url = "www.emodnet-biology.eu",                  
  # geospatial_bounds = "",              
  # geospatial_bounds_crs = "",          
  # geospatial_bounds_vertical_crs = "", 
  geospatial_lat_min = min(lat),
  geospatial_lat_max = max(lat),
  geospatial_lon_min = min(lon),
  geospatial_lon_max = max(lon),
  # geospatial_vertical_min = "",        
  # geospatial_vertical_max = "",        
  # geospatial_vertical_positive = "",  
  # time_coverage_start = "1911",            
  # time_coverage_end = "2016",              
  # time_coverage_duration = "",         
  # time_coverage_resolution = "",       
  # uuid = "",                           
  # sea_name = "",                       
  # creator_type = "",                   
  creator_institution = "Flanders Marine Institute (VLIZ)",            
  # publisher_type = "",                 
  publisher_institution = "Flanders Marine Institute (VLIZ)",        
  # program = "",                        
  # contributor_name = "",               
  # contributor_role  = "",              
  geospatial_lat_units = "degrees_north",           
  geospatial_lon_units = "degrees_east",           
  # geospatial_vertical_units   = "",    
  # date_modified = "",               
  # date_issued = "",                    
  # date_metadata_modified   = "",       
  # product_version = "",            
  # keywords_vocabulary = "",          
  # platform  = "",              
  # platform_vocabulary = "",          
  # instrument = "",          
  # instrument_vocabulary  = "",        
  # featureType = "Point",                  
  # metadata_link = "",                  
  # references = "",
  comment = "Uses attributes recommended by http://cfconventions.org",
  license = "CC-BY", 
  publisher_name = "EMODnet Biology Data Management Team",
  citation = "Fernández-Bejarano, Salvador. 2022. Create a EMODnet-Biology data product as NetCDF.",
  acknowledgement = "European Marine Observation Data Network (EMODnet) Biology project (EMFF/2019/1.3.1.9/Lot 6/SI2.837974), funded by the European Union under Regulation (EU) No 508/2014 of the European Parliament and of the Council of 15 May 2014 on the European Maritime and Fisheries Fund"
)

# Define function that detects if the data type should be character of 
# integer and add to global attributes
add_global_attributes <- function(nc, attributes){
  
  stopifnot(is.list(attributes))
  
  for(i in 1:length(attributes)){
    if(is.character(attributes[[i]])){
      type <- "NC_CHAR"
    }else if(is.numeric(attributes[[i]])){
      type <- "NC_DOUBLE"
    }
    att.put.nc(nc, variable = "NC_GLOBAL", name = names(attributes[i]), type = type, value = attributes[[i]])
  }
  sync.nc(nc)
}

# Add attributes
add_global_attributes(nc, attributes)

4.5 Wrapping up

Congratulations! The NetCDF file containing the product has been created. Now you can inspect the file.

sync.nc(nc)
print.nc(nc)
#> netcdf classic {
#> dimensions:
#>  lon = 10 ;
#>  lat = 10 ;
#>  time = 3 ;
#>  aphiaid = 4 ;
#>  string80 = 80 ;
#> variables:
#>  NC_DOUBLE lon(lon) ;
#>      NC_CHAR lon:units = "degrees_east" ;
#>      NC_CHAR lon:standard_name = "longitude" ;
#>      NC_CHAR lon:long_name = "Longitude" ;
#>  NC_DOUBLE lat(lat) ;
#>      NC_CHAR lat:units = "degrees_north" ;
#>      NC_CHAR lat:standard_name = "latitude" ;
#>      NC_CHAR lat:long_name = "Latitude" ;
#>  NC_DOUBLE time(time) ;
#>      NC_CHAR time:standard_name = "time" ;
#>      NC_CHAR time:long_name = "Time" ;
#>      NC_CHAR time:units = "days since 1970-01-01 00:00:00" ;
#>      NC_CHAR time:calendar = "gregorian" ;
#>  NC_INT aphiaid(aphiaid) ;
#>      NC_CHAR aphiaid:long_name = "Life Science Identifier - World Register of Marine Species" ;
#>  NC_CHAR taxon_name(string80, aphiaid) ;
#>      NC_CHAR taxon_name:standard_name = "biological_taxon_name" ;
#>      NC_CHAR taxon_name:long_name = "Scientific name of the taxa" ;
#>  NC_CHAR taxon_lsid(string80, aphiaid) ;
#>      NC_CHAR taxon_lsid:standard_name = "biological_taxon_lsid" ;
#>      NC_CHAR taxon_lsid:long_name = "Life Science Identifier - World Register of Marine Species" ;
#>  NC_CHAR crs ;
#>      NC_CHAR crs:long_name = "Coordinate Reference System" ;
#>      NC_CHAR crs:geographic_crs_name = "WGS 84" ;
#>      NC_CHAR crs:grid_mapping_name = "latitude_longitude" ;
#>      NC_CHAR crs:reference_ellipsoid_name = "WGS 84" ;
#>      NC_CHAR crs:horizontal_datum_name = "WGS 84" ;
#>      NC_CHAR crs:prime_meridian_name = "Greenwich" ;
#>      NC_DOUBLE crs:longitude_of_prime_meridian = 0 ;
#>      NC_DOUBLE crs:semi_major_axis = 6378137 ;
#>      NC_DOUBLE crs:semi_minor_axis = 6356752.31424518 ;
#>      NC_DOUBLE crs:inverse_flattening = 298.257223563 ;
#>      NC_CHAR crs:spatial_ref = "GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AXIS["Latitude",NORTH],AXIS["Longitude",EAST],AUTHORITY["EPSG","4326"]]" ;
#>      NC_CHAR crs:GeoTransform = "-180 0.08333333333333333 0 90 0 -0.08333333333333333 " ;
#>  NC_INT presence_absence(lon, lat, time, aphiaid) ;
#>      NC_INT presence_absence:_FillValue = -99999 ;
#>      NC_CHAR presence_absence:long_name = "Probability of occurrence of biological entity" ;
#> 
#> // global attributes:
#>      NC_CHAR :title = "Example title" ;
#>      NC_CHAR :summary = "This is the result of a demo to show how to turn a variable defined by four dimensions into a netcdf array." ;
#>      NC_CHAR :Conventions = "CF-1.8" ;
#>      NC_CHAR :naming_authority = "emodnet-biology.eu" ;
#>      NC_CHAR :history = "https://github.com/EMODnet/EMODnet-Biology-products-erddap-demo" ;
#>      NC_CHAR :source = "https://github.com/EMODnet/EMODnet-Biology-products-erddap-demo" ;
#>      NC_CHAR :license = "CC-BY" ;
#>      NC_CHAR :standard_name_vocabulary = "CF Standard Name Table v1.8" ;
#>      NC_CHAR :date_created = "2023-02-16" ;
#>      NC_CHAR :creator_name = "Salvador Fernandez" ;
#>      NC_CHAR :creator_email = "salvador.fernandez@vliz.be" ;
#>      NC_CHAR :creator_url = "www.vliz.be" ;
#>      NC_CHAR :institution = "Flanders Marine Institute (VLIZ)" ;
#>      NC_CHAR :project = "EMODnet-Biology" ;
#>      NC_CHAR :publisher_name = "EMODnet Biology Data Management Team" ;
#>      NC_CHAR :publisher_email = "bio@emodnet.eu" ;
#>      NC_CHAR :publisher_url = "www.emodnet-biology.eu" ;
#>      NC_DOUBLE :geospatial_lat_min = -24.347 ;
#>      NC_DOUBLE :geospatial_lat_max = 67.85716 ;
#>      NC_DOUBLE :geospatial_lon_min = -44.165 ;
#>      NC_DOUBLE :geospatial_lon_max = 10.343 ;
#>      NC_CHAR :creator_institution = "Flanders Marine Institute (VLIZ)" ;
#>      NC_CHAR :publisher_institution = "Flanders Marine Institute (VLIZ)" ;
#>      NC_CHAR :geospatial_lat_units = "degrees_north" ;
#>      NC_CHAR :geospatial_lon_units = "degrees_east" ;
#>      NC_CHAR :comment = "Uses attributes recommended by http://cfconventions.org" ;
#>      NC_CHAR :citation = "Fernández-Bejarano, Salvador. 2022. Create a EMODnet-Biology data product as NetCDF." ;
#>      NC_CHAR :acknowledgement = "European Marine Observation Data Network (EMODnet) Biology project (EMFF/2019/1.3.1.9/Lot 6/SI2.837974), funded by the European Union under Regulation (EU) No 508/2014 of the European Parliament and of the Council of 15 May 2014 on the European Maritime and Fisheries Fund" ;
#> }

You can read the variables you have created with var.get.nc

# Get variable, turn array into vector and get unique values
unique(c(var.get.nc(nc, variable = "presence_absence")))
#> [1] NA  0  1

If everything is correct, close the file. Remember it was saved into ./data/derived/foo.nc

close.nc(nc)
LS0tDQp0aXRsZTogIkNyZWF0ZSBhIEVNT0RuZXQtQmlvbG9neSBkYXRhIHByb2R1Y3QgYXMgTmV0Q0RGIg0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjb2RlX2ZvbGRpbmc6ICJzaG93Ig0KICAgIGRmX3ByaW50OiAndGliYmxlJw0KICAgIG51bWJlcl9zZWN0aW9uOiB5ZXMNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogMw0KICAgIHRvY19mbG9hdDoNCiAgICAgIGNvbGxhcHNlZDogeWVzDQogICAgICBzbW9vdGhfc2Nyb2xsOiB5ZXMNCg0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KA0KICBlY2hvID0gVFJVRSwgDQogIGNvbGxhcHNlID0gVFJVRSwNCiAgY29tbWVudCA9ICIjPiIpDQpgYGANCg0KIyBTZXR0aW5ncw0KDQpUaGVyZSBhcmUgZm91ciBkZXBlbmRlbmNpZXMgdXNlZCBpbiB0aGlzIGRlbW8uIFRoZSBtb3N0IGltcG9ydGFudCBpcyB0aGUgW1JOZXRDREZdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9STmV0Q0RGL2luZGV4Lmh0bWwpIHBhY2thZ2UuIFRoaXMgaXMgYSBsb3ctbGV2ZWwgUiBpbnRlcmZhY2UgdG8gdGhlIGZpbGUgZm9ybWF0IE5ldENERiBkZXNpZ25lZCBieSBVbmlkYXRhLiBPdGhlciBwYWNrYWdlcyB0byBpbnRlcmFjdCB3aXRoIE5ldENERiBpbiBSIGFyZSBbbmNkZjRdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9uY2RmNC8pIGFuZCBbdGlkeW5jXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvdGlkeW5jLykuDQoNClRoZSByZXN0IG9mIGxpYnJhcmllcyBhcmUgdXNlZCBhcyBoZWxwZXJzLg0KDQpgYGB7ciBsaWJyYXJpZXMsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQpsaWJyYXJ5KFJOZXRDREYpDQpsaWJyYXJ5KHJlYWRyKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoZ2x1ZSkNCmBgYA0KDQoNCiMgQW4gZXhhbXBsZSBkYXRhIHNldA0KDQpUaGlzIGlzICoqYSBmcmFnbWVudCBvZiBhIHJlYWwgZGF0YSBzZXQqKiwgYWRqdXN0ZWQgdG8gc2VydmUgYXMgYSBzbWFsbCBleGFtcGxlLiBUaGUgbmFtZXMgb2YgdGhlIGNvbHVtbnMgYXJlIFtEYXJ3aW4gQ29yZSB0ZXJtc10oaHR0cHM6Ly9kd2MudGR3Zy5vcmcvdGVybXMpDQoNCg0KDQpgYGB7ciByZWFkfQ0KZGF0YXNldCA8LSByZWFkX2NzdigiLi9kYXRhL3Jhdy9kYXRhc2V0LmNzdiIpDQoNCmRhdGFzZXQNCmBgYA0KDQoNCiMgQXJyYW5nZSB0aGUgZGF0YSBzZXQNCg0KVGhlIHZhcmlhYmxlIG9mIGludGVyZXN0IGlzIGBvY2N1cnJlbmNlU3RhdHVzYC4gKipJdCBoYXMgZWl0aGVyIHByZXNlbmNlcyAoMSkgb3IgYWJzZW5jZXMgKDApKioNCg0KSXQgY2FuIGJlIGNvbnNpZGVyZWQgdGhhdCB0aGlzIHZhcmlhYmxlIGlzIGRlZmluZWQgYnkgKipmb3VyIGRpbWVuc2lvbnMqKjogKipTcGF0aWFsbHkqKiwgdGhlc2UgYXJlIGBkZWNpbWFsTG9uZ2l0dWRlYCBhbmQgYGRlY2ltYWxMYXRpdHVkZWAuICoqVGltZXdpc2UqKiBpcyB0aGUgY29sdW1uIGBldmVudERhdGVgLiANCg0KVGhlIGZvdXJ0aCBkaW1lbnNpb24gdGhhdCBkZWZpbmVzIHRoZSB2YXJpYWJsZSBvZiBpbnRlcmVzdCBoZXJlIGlzIHRoZSAqKnRheG9uKiosIHdoaWNoIGlzIHNob3duIGluIHRoZSBjb2x1bW5zIGBzY2llbnRpZmljTmFtZWAsIGBzY2llbnRpZmljTmFtZUlEYCBhbmQgYEFwaGlhSURgLiBUaGUgZmlyc3QgaXMgdGhlIHNjaWVudGlmaWMgbmFtZSBvZiB0aGUgdGF4YSwgd2hpbGUgYHNjaWVudGlmaWNOYW1lSURgIGhhcyB0aGUgTGlmZSBTY2llbmNlIElkZW50aWZpZXIgKExTSUQpIGFzIGFuIHVyaS4gVGhlIGNvbHVtbiBgQXBoaWFJRGAgaXMgdGhlIHVuaXF1ZSBpZGVudGlmaWVyIGZvciBhIHRheG9uIG5hbWUgdXNlZCBpbiB0aGUgW1dvcmxkIFJlY29yZCBvZiBNYXJpbmUgU3BlY2llc10oaHR0cHM6Ly93d3cubWFyaW5lc3BlY2llcy5vcmcvKS4NCg0KDQojIyBUZWNobmljYWwgY29uc3RyYWluOiBFZGl0IGBldmVudERhdGVgDQoNClRoZSBgZXZlbnREYXRlYCAqKm11c3QgYmUgdHJhbnNmb3JtZWQgdG8gdGVtcG9yYWwgYW1vdW50cyoqLiBUaGVzZSBhcmUgc2Vjb25kcywgZGF5cywgeWVhcnMgZXRjIHNpbmNlIGEgY2VydGFpbiBkYXRlLiBUaGVyZSBpcyBhICoqaGVscGVyKiogaW4gdGhlIFJOZXRDREYgcGFja2FnZTogYHVpdGludmNhbC5uYygpYA0KDQpJbiB0aGlzIGRlbW8sIHRoZSB1bml0cyB3aWxsIGJlIGBkYXlzIHNpbmNlIDE5NzAtMDEtMDEgMDA6MDA6MDBgLg0KDQpgYGB7ciBkYXRlfQ0KZGF0YXNldCR0aW1lIDwtIHV0aW52Y2FsLm5jKA0KICB1bml0c3RyaW5nID0gImRheXMgc2luY2UgMTk3MC0wMS0wMSAwMDowMDowMCIgLCANCiAgdmFsdWUgPSBhcy5QT1NJWGN0KGRhdGFzZXQkZXZlbnREYXRlLCB0eiA9ICJVVEMiKQ0KKQ0KDQp1bmlxdWUoZGF0YXNldCRldmVudERhdGUpDQp1bmlxdWUoZGF0YXNldCR0aW1lKQ0KYGBgDQoNCg0KIyMgR2V0IGFsbCBwb3NzaWJsZSBjb21iaW5hdGlvbiBvZiB0aGUgZGltZW5zaW9ucw0KDQpUaGUga2V5IHRvIHRyYW5zZm9ybSBhIGRhdGEgZnJhbWUgdG8gbmV0Y2RmLCBpcyB0aGF0ICoqdGhlIGRhdGEgbXVzdCBiZSB0cmFuc2Zvcm1lZCBpbnRvIGEgNEQgYXJyYXkqKi4gVG8gZG8gdGhhdCwgKiphbGwgdGhlIHBvc3NpYmxlIGNvbWJpbmF0aW9ucyBvZiB0aGUgZGltZW5zaW9ucyBtdXN0IGJlIGF0dGFjaGVkKiouIFRoaXMgd2lsbCBjb2VyY2UgYE5BYCBvciBlbXB0eSB2YWx1ZXMgaW4gdGhlIHZhcmlhYmxlIG9mIGludGVyZXN0IGFzIHRoZXJlIHdpbGwgYmUgbm8gdmFsdWUgZm9yIHRoZXNlIGNvbWJpbmF0aW9ucy4NCg0KVGhlIGJhc2UgZnVuY3Rpb24gYGV4cGFuZC5ncmlkKClgIGFsbG93cyB0byBwYXNzIHZlY3RvcnMgYW5kIGNyZWF0ZSBhIGRhdGEgZnJhbWUgd2l0aCBhbGwgdGhlIHBvc3NpYmxlIGNvbWJpbmF0aW9ucyBvZiB0aG9zZSB2ZWN0b3JzLg0KDQpgYGB7ciBjb2VyY2VfaW50b19hcnJheX0NCiMgRmlyc3QgYWRkIGFuIHVuaXF1ZSBpZGVudGlmaWVyIGJ5IHRoZSBjb21iaW5hdGlvbiBvZjogDQojIGRlY2ltYUxvbmdpdHVkZSwgZGVjaW1hbExhdGl0dWRlLCBldmVudERhdGUgYW5kIEFwaGlhSUQNCmRhdGFzZXQgPC0gZGF0YXNldCAlPiUNCiAgbXV0YXRlKA0KICAgIGlkID0gZ2x1ZSgie2RlY2ltYWxMb25naXR1ZGV9LXtkZWNpbWFsTGF0aXR1ZGV9LXt0aW1lfS17QXBoaWFJRH0iKQ0KICApDQoNCiMgRXh0cmFjdCB0aGUgdW5pcXVlIGFuZCBzb3J0ZWQgdmFsdWVzIG9mIHRoZSA0IGRpbWVuc2lvbnMNCmxvbiA9IHNvcnQodW5pcXVlKGRhdGFzZXQkZGVjaW1hbExvbmdpdHVkZSkpDQpsYXQgPSBzb3J0KHVuaXF1ZShkYXRhc2V0JGRlY2ltYWxMYXRpdHVkZSkpDQp0aW1lID0gc29ydCh1bmlxdWUoZGF0YXNldCR0aW1lKSkNCg0KIyBUYXhvbiB3aWxsIGJlIHB1dCBpbiBhIG5ldyBkYXRhIGZyYW1lDQp0YXhvbiA8LSB0aWJibGUoDQogIGFwaGlhaWQgPSBkYXRhc2V0JEFwaGlhSUQsDQogIHRheG9uX25hbWUgPSBkYXRhc2V0JHNjaWVudGlmaWNOYW1lLA0KICB0YXhvbl9sc2lkID0gZGF0YXNldCRzY2llbnRpZmljTmFtZUlEKSAlPiUgDQogIGRpc3RpbmN0KCkgJT4lDQogIGFycmFuZ2UoYXBoaWFpZCkNCg0KIyBVc2UgZXhwYW5kLmdyaWQoKSB0byBjcmVhdGUgYSBkYXRhIGZyYW1lIHdpdGggYWxsIHRoZSBwb3NzaWJsZSANCiMgY29tYmluYXRpb25zIG9mIHRoZSA0IGRpbWVuc2lvbnMNCmxvbmdlciA8LSBleHBhbmQuZ3JpZChsb24gPSBsb24sIGxhdCA9IGxhdCwgdGltZSA9IHRpbWUsIA0KICAgICAgICAgICAgICAgICAgICAgYXBoaWFpZCA9IHRheG9uJGFwaGlhaWQsIA0KICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQ0KDQojIERlZmluZSB1bmlxdWUgaWRlbnRpZmllciBhZ2FpbiBhbmQgbWVyZ2UgdGhlIHZhcmlhYmxlIG9jY3VycmVuY2VTdGF0dXMgDQojIHdpdGggcHJlc2VuY2VzIGFuZCBhYnNlbmNlcw0KDQpkYXRhc2V0IDwtIGRhdGFzZXQgJT4lDQogIHNlbGVjdChpZCwgb2NjdXJyZW5jZVN0YXR1cykNCg0KbG9uZ2VyIDwtIGxvbmdlciAlPiUgDQogIG11dGF0ZSgNCiAgICBpZCA9IGdsdWUoIntsb259LXtsYXR9LXt0aW1lfS17YXBoaWFpZH0iKQ0KICApICU+JQ0KICBsZWZ0X2pvaW4oZGF0YXNldCkgJT4lDQogIHNlbGVjdCgtaWQpDQoNCiMgU2F2ZSBmb3IgbGF0ZXINCndyaXRlX2Nzdihsb25nZXIsICIuL2RhdGEvZGVyaXZlZC9sb25nZXIuY3N2IikNCg0KIyBJbnNwZWN0DQpsb25nZXINCmBgYA0KDQoNCiMjIFR1cm4gZGF0YSBmcmFtZSBpbnRvIGEgNEQgYXJyYXkNCg0KKipOZXRDREYgaXMgZGVzaWduZWQgdG8gaG9zdCBhcnJheS1vcmllbnRlZCBzY2llbnRpZmljIGRhdGEqKiAoW3JlZl0oaHR0cHM6Ly9kb2NzLnVuaWRhdGEudWNhci5lZHUvbmV0Y2RmLWMvY3VycmVudC8pKS4gVHJhbnNmb3JtaW5nIGludG8gYW4gb2JqZWN0IG9mIHR5cGUgYGFycmF5YCBpbiBSIHdpbGwgYWxsb3cgdGhlIGVhc2llc3Qgd2F5IHRvIGFkZCBkYXRhIGludG8gYSBOZXRDREYgZmlsZS4gDQoNCkluIHRoZSBkZW1vIGRhdGFzZXQsIHdlIGtub3cgdGhhdCB0aGUgKip0b3RhbCBsZW5ndGggb2YgZWFjaCBkaW1lbnNpb24qKiBhcmU6DQoNCiogbG9uOiAxMA0KKiBsYXQ6IDEwDQoqIHRpbWU6IDMNCiogdGF4YTogNA0KDQpUaGVzZSBsZW5ndGhzIHdpbGwgYmUgcGFzc2VkIHRvIHRoZSBSIGZ1bmN0aW9uIGBhcnJheSgpYCBhcyBhIHZlY3RvcjogYGMoMTAsIDEwLCAzLCA0KWAuIFdlIHdpbGwgYWxzbyBwYXNzIHRoZSB2YXJpYWJsZSBvZiBpbnRlcmVzdCBgbG9uZ2VyJG9jY3VycmVuY2VTdGF0dXNgLiBIb3dldmVyLCB0aGlzIHZhcmlhYmxlIG11c3QgaGF2ZSBhcyBsZW5ndGggdGhlIHByb2R1Y3Qgb2YgYWxsIGxlbmd0aHMgb2YgYWxsIGRpbWVuc2lvbnM6ICoqVGhpcyBpcyB3aHkgd2UgZ290IGFsbCB0aGUgY29tYmluYXRpb25zIG9mIHRoZSBkaW1lbnNpb25zKiogdmlhIGBleHBhbmQuZ3JpZCgpYC4NCg0KYGBge3IgY3JlYXRlX2FycmF5fQ0KIyBUaGUgcHJvZHVjdCBvZiB0aGUgbGVuZ3RocyBvZiBhbGwgZGltZW5zaW9ucw0KMTAgKiAxMCAqIDMgKiA0DQoNCiMgSXMgdGhlIHNhbWUgYXMgdGhlIGxlbmd0aCBvZiB0aGUgdmFyaWFibGUgb2YgaW50ZXJlc3QsIGluY2x1ZGluZyBhbGwgDQojIHBvc3NpYmxlIGNvbWJpbmF0aW9ucyBvZiB0aGUgZGltZW5zaW9ucyBldmVuIGlmIHRoaXMgY29lcmNlIE5BJ3MNCmxlbmd0aChsb25nZXIkb2NjdXJyZW5jZVN0YXR1cykNCg0KIyBDcmVhdGUgYXJyYXkNCmFycmF5IDwtIGFycmF5KA0KICBkYXRhID0gbG9uZ2VyJG9jY3VycmVuY2VTdGF0dXMsDQogIGRpbSA9IGMoMTAsIDEwLCAzLCA0KQ0KKQ0KYGBgDQoNCk5vdGUgdGhhdCB0aGlzIHdvbid0IGFsd2F5cyBiZSBwb3NzaWJsZSBhcyAqKmFkZGluZyBhbGwgdGhlIHBvc3NpYmxlIGNvbWJpbmF0aW9ucyBtYWtlcyB0aGUgdmFyaWFibGUgb2YgaW50ZXJlc3QgZ3JvdyBleHBvbmVudGlhbGx5KiouIEluIFIsIHZlY3RvcnMsIGRhdGEgZnJhbWVzIGFuZCBhcnJheXMgYXJlIHJlYWQgaW50byBtZW1vcnkuIEl0IGlzIGNvbW1vbiB0aGF0ICoqeW91ciBtYWNoaW5lIHdvbid0IGhhdmUgZW5vdWdoIG1lbW9yeSoqIHRvIGhhbmRsZSBzdWNoIGFtb3VudCBvZiBkYXRhLg0KDQpCdXQgd29ycnkgbm90OiAqKlRoZXJlIGFyZSB3b3JrYXJvdW5kcyoqLiBUaGVzZSBhcmUgZGlzY3Vzc2VkIGxhdGVyIGJlbG93IGluIHRoaXMgZGVtby4NCg0KDQojIFRyYW5zZm9ybSBpbnRvIE5ldENERg0KDQojIyBDcmVhdGUgYSBwbGFjZWhvbGRlcg0KDQpUaGUgZmlyc3Qgc3RlcCBpcyB0byAqKmNyZWF0ZSBhIG5ldGNkZiBmaWxlKiogdGhhdCB3aWxsIHdvcmsgYXMgYSBwbGFjZWhvbGRlciB0byBhZGQgdGhlIGRhdGEuIFdlIHVzZSB0aGUgUiBwYWNrYWdlIGBSTmV0Q0RGYA0KDQpgYGB7ciBjcmVhdGVfbmN9DQojIENyZWF0ZSBuYyBmaWxlDQpuYyA8LSBjcmVhdGUubmMoIi4vZGF0YS9kZXJpdmVkL2Zvby5uYyIpIA0KYGBgDQoNCg0KIyMgRGVmaW5lIGRpbWVuc2lvbnMNCg0KKipFYWNoIGRpbWVuc2lvbnMgaGFzIGFuIGhvbW9ueW1vdXMgdmFyaWFibGUgYXNzaWduZWQqKiBhbG9uZyB3aXRoIG51bWJlciBvZiBhdHRyaWJ1dGVzLiBUaGlzIGlzIGEgdGVjaG5pY2FsIHJlcXVpcmVtZW50LiBUaGUgZGltZW5zaW9ucyBtdXN0IGhhdmUgdGhlIGxlbmd0aCBvZiB0aGUgdW5pcXVlIHZhbHVlcyBvZiBlYWNoIGRpbWVuc2lvbi4NCg0KU29tZSBleHRyYSBhdHRyaWJ1dGVzIG11c3QgYmUgYWRkZWQgdG8gbWVldCB0aGUgW0NGLUNvbnZlbnRpb25dKGh0dHA6Ly9jZmNvbnZlbnRpb25zLm9yZy8pLg0KDQpJbiBhZGRpdGlvbiwgdGhlIGhvbW9ueW1vdXMgdmFyaWFibGUgZGVmaW5pbmcgdGhlIGRpbWVuc2lvbnMgKiptdXN0IGhhdmUgdGhlaXIgb3duIGRhdGEqKi4gVGhlIGRhdGEgYXJlIHBhc3NlZCBhcyBhIDFEIHZlY3RvciB0byBgdmFyLnB1dC5uYygpYCwgc3BlY2lmeWluZyBpbiB3aGljaCB2YXJpYWJsZSBoYXMgdG8gYmUgd3JpdHRlbi4NCg0KIyMjIExvbmdpdHVkZQ0KYGBge3IgbG9ufQ0KIyBEZWZpbmUgbG9uIGRpbWVuc2lvbg0KZGltLmRlZi5uYyhuYywgZGltbmFtZSA9ICJsb24iLCBkaW1sZW5ndGggPSBsZW5ndGgobG9uKSkgDQoNCiMgRGVmaW5lIGxvbiB2YXJpYWJsZQ0KdmFyLmRlZi5uYyhuYywgdmFybmFtZSA9ICJsb24iLCB2YXJ0eXBlID0gIk5DX0RPVUJMRSIsIGRpbWVuc2lvbnMgPSAibG9uIikNCg0KIyBBZGQgYXR0cmlidXRlcw0KYXR0LnB1dC5uYyhuYywgdmFyaWFibGUgPSAibG9uIiwgbmFtZSA9ICJ1bml0cyIsIHR5cGUgPSAiTkNfQ0hBUiIsIHZhbHVlID0gImRlZ3JlZXNfZWFzdCIpDQphdHQucHV0Lm5jKG5jLCB2YXJpYWJsZSA9ICJsb24iLCBuYW1lID0gInN0YW5kYXJkX25hbWUiLCB0eXBlID0gIk5DX0NIQVIiLCB2YWx1ZSA9ICJsb25naXR1ZGUiKQ0KYXR0LnB1dC5uYyhuYywgdmFyaWFibGUgPSAibG9uIiwgbmFtZSA9ICJsb25nX25hbWUiLCB0eXBlID0gIk5DX0NIQVIiLCB2YWx1ZSA9ICJMb25naXR1ZGUiKQ0KDQojIFB1dCBkYXRhDQp2YXIucHV0Lm5jKG5jLCB2YXJpYWJsZSA9ICJsb24iLCBkYXRhID0gbG9uKSANCg0KIyBDaGVjaw0KdmFyLmdldC5uYyhuYywgdmFyaWFibGUgPSAibG9uIikNCmBgYA0KDQojIyMgTGF0aXR1ZGUNCmBgYHtyIGxhdH0NCiMgRGVmaW5lIGxhdCBkaW1lbnNpb24NCmRpbS5kZWYubmMobmMsIGRpbW5hbWUgPSAibGF0IiwgZGltbGVuZ3RoID0gbGVuZ3RoKGxhdCkpIA0KDQojIERlZmluZSBsYXQgdmFyaWFibGUNCnZhci5kZWYubmMobmMsIHZhcm5hbWUgPSAibGF0IiwgdmFydHlwZSA9ICJOQ19ET1VCTEUiLCBkaW1lbnNpb25zID0gImxhdCIpDQoNCiMgQWRkIGF0dHJpYnV0ZXMNCmF0dC5wdXQubmMobmMsIHZhcmlhYmxlID0gImxhdCIsIG5hbWUgPSAidW5pdHMiLCB0eXBlID0gIk5DX0NIQVIiLCB2YWx1ZSA9ICJkZWdyZWVzX25vcnRoIikNCmF0dC5wdXQubmMobmMsIHZhcmlhYmxlID0gImxhdCIsIG5hbWUgPSAic3RhbmRhcmRfbmFtZSIsIHR5cGUgPSAiTkNfQ0hBUiIsIHZhbHVlID0gImxhdGl0dWRlIikNCmF0dC5wdXQubmMobmMsIHZhcmlhYmxlID0gImxhdCIsIG5hbWUgPSAibG9uZ19uYW1lIiwgdHlwZSA9ICJOQ19DSEFSIiwgdmFsdWUgPSAiTGF0aXR1ZGUiKQ0KDQojIFB1dCBkYXRhDQp2YXIucHV0Lm5jKG5jLCB2YXJpYWJsZSA9ICJsYXQiLCBkYXRhID0gbGF0KSANCg0KIyBDaGVjaw0KdmFyLmdldC5uYyhuYywgdmFyaWFibGUgPSAibGF0IikNCmBgYA0KDQoNCiMjIyBUaW1lDQpgYGB7ciB0aW1lfQ0KIyBEZWZpbmUgdGltZSBkaW1lbnNpb24NCmRpbS5kZWYubmMobmMsIGRpbW5hbWUgPSAidGltZSIsIGRpbWxlbmd0aCA9IGxlbmd0aCh0aW1lKSkgDQoNCiMgRGVmaW5lIHRpbWUgdmFyaWFibGUNCnZhci5kZWYubmMobmMsIHZhcm5hbWUgPSAidGltZSIsIHZhcnR5cGUgPSAiTkNfRE9VQkxFIiwgZGltZW5zaW9ucyA9ICJ0aW1lIikNCg0KIyBBZGQgYXR0cmlidXRlcw0KYXR0LnB1dC5uYyhuYywgdmFyaWFibGUgPSAidGltZSIsIG5hbWUgPSAic3RhbmRhcmRfbmFtZSIsIHR5cGUgPSAiTkNfQ0hBUiIsIHZhbHVlID0gInRpbWUiKQ0KYXR0LnB1dC5uYyhuYywgdmFyaWFibGUgPSAidGltZSIsIG5hbWUgPSAibG9uZ19uYW1lIiwgdHlwZSA9ICJOQ19DSEFSIiwgdmFsdWUgPSAiVGltZSIpDQphdHQucHV0Lm5jKG5jLCB2YXJpYWJsZSA9ICJ0aW1lIiwgbmFtZSA9ICJ1bml0cyIsIHR5cGUgPSAiTkNfQ0hBUiIsIHZhbHVlID0gImRheXMgc2luY2UgMTk3MC0wMS0wMSAwMDowMDowMCIpDQphdHQucHV0Lm5jKG5jLCB2YXJpYWJsZSA9ICJ0aW1lIiwgbmFtZSA9ICJjYWxlbmRhciIsIHR5cGUgPSAiTkNfQ0hBUiIsIHZhbHVlID0gImdyZWdvcmlhbiIpDQoNCiMgUHV0IGRhdGENCnZhci5wdXQubmMobmMsIHZhcmlhYmxlID0gInRpbWUiLCBkYXRhID0gdGltZSkNCg0KIyBDaGVjaw0KdmFyLmdldC5uYyhuYywgdmFyaWFibGUgPSAidGltZSIpDQpgYGANCg0KDQojIyMgVGF4b24NCg0KVG8gZGVmaW5lIHRoZSB0YXhvbiBkaW1lbnNpb24sIHRoZXJlIHdpbGwgYmUgb25lIGRpbWVuc2lvbiBjYWxsZWQgYGFwaGlhaWRgLCAqKndpdGggdGhyZWUgdmFyaWFibGVzIGFzc2lnbmVkOiAqKg0KDQotIGBhcGhpYWlkYDogdGhlIGFjdHVhbCBhcGhpYWlkIGFzIGFuIGludGVnZXINCi0gYHRheG9uX25hbWVgOiB0aGUgc2NpZW50aWZpYyBuYW1lIG9mIHRoZSB0YXhhDQotIGB0YXhvbl9sc2lkYDogdGhlIExTSUQgKHVuaXF1ZSBpZGVudGlmaWVyKSBvZiB0aGUgdGF4YQ0KDQoqKlRoZXNlIGFyZSB0ZWNobmljYWwgcmVxdWlyZW1lbnRzKiogYm90aCBmb3IgRVJEREFQIGFuZCB0byBtZWV0IHRoZSBbQ0YtQ29udmVudGlvbl0oaHR0cDovL2NmY29udmVudGlvbnMub3JnLykuDQoNClRoZSBkaW1lbnNpb24gYGFwaGlhaWRgIHdpbGwgYmUgdXNlZCBsYXRlciB0byBkZWZpbmUgdGhlIHZhcmlhYmxlIG9mIGludGVyZXN0LiBJdCBpcyBhIGJldHRlciBwcmFjdGljZSB0byB1c2UgdGhlIEFwaGlhSUQgaW5zdGVhZCBvZiwgZm9yIGluc3RhbmNlLCB0aGUgc2NpZW50aWZpYyBuYW1lIGluIGB0YXhvbl9uYW1lYCwgYmVjYXVzZSAqKk5ldENERiB3b3JrcyBiZXR0ZXIgd2l0aCBudW1lcmljIGRhdGEgdHlwZXMgdGhhbiB3aXRoIGNoYXJhY3RlcnMuKiogDQoNCk5vdGUgdGhhdCAqKmNoYXJhY3RlciB2YXJpYWJsZXMgaW4gTmV0Q0RGIHJlcXVpcmUgdG8gYmUgZGVmaW5lZCBhbHNvIGJ5IGEgZGltZW5zaW9uIHR5cGljYWxseSBjYWxsZWQgInN0cmluZyIqKi4gSXRzIGxlbmd0aCBpcyB0aGUgKip0b3RhbCBudW1iZXIgb2YgY2hhcmFjdGVycyoqIHRoYXQgY2FuIGJlIGhvc3RlZCBpbiB0aGUgdmFyaWFibGUgb2YgdHlwZSBjaGFyYWN0ZXIuIFRoaXMgaXMgYSByZXF1aXJlbWVudCBvZiBOZXRDREY0Lg0KDQoNCiMjIyMgYXBoaWFpZA0KYGBge3IgYXBoaWFpZH0NCiMgRGVmaW5lIHRoZSBhcGhpYSBhbmQgc3RyaW5nODAgZGltZW5zaW9ucw0KZGltLmRlZi5uYyhuYywgZGltbmFtZSA9ICJhcGhpYWlkIiwgZGltbGVuZ3RoID0gbnJvdyh0YXhvbikpDQpkaW0uZGVmLm5jKG5jLCBkaW1uYW1lID0gInN0cmluZzgwIiwgZGltbGVuZ3RoID0gODApDQoNCiMgQWRkIGFwaGlhaWQgdmFyaWFibGUgYW5kIGF0dHJpYnV0ZXMgDQp2YXIuZGVmLm5jKG5jLCB2YXJuYW1lID0gImFwaGlhaWQiLCB2YXJ0eXBlID0gIk5DX0lOVCIsIGRpbWVuc2lvbnMgPSAiYXBoaWFpZCIpDQphdHQucHV0Lm5jKG5jLCB2YXJpYWJsZSA9ICJhcGhpYWlkIiwgbmFtZSA9ICJsb25nX25hbWUiLCB0eXBlID0gIk5DX0NIQVIiLCB2YWx1ZSA9ICJMaWZlIFNjaWVuY2UgSWRlbnRpZmllciAtIFdvcmxkIFJlZ2lzdGVyIG9mIE1hcmluZSBTcGVjaWVzIikNCg0KIyBQdXQgYXBoaWFpZCBkYXRhDQp2YXIucHV0Lm5jKG5jLCB2YXJpYWJsZSA9ICJhcGhpYWlkIiwgZGF0YSA9IHRheG9uJGFwaGlhaWQpDQoNCiMgQ2hlY2sNCnZhci5nZXQubmMobmMsIHZhcmlhYmxlID0gImFwaGlhaWQiKQ0KYGBgDQoNCg0KIyMjIyB0YXhvbl9uYW1lDQpgYGB7ciB0YXhvbl9uYW1lfQ0KIyBBZGQgdGF4b25fbmFtZSB2YXJpYWJsZSBhbmQgYXR0cmlidXRlcw0KdmFyLmRlZi5uYyhuYywgdmFybmFtZSA9ICJ0YXhvbl9uYW1lIiwgdmFydHlwZSA9ICJOQ19DSEFSIiwgZGltZW5zaW9uID0gYygic3RyaW5nODAiLCAiYXBoaWFpZCIpKQ0KYXR0LnB1dC5uYyhuYywgdmFyaWFibGUgPSAidGF4b25fbmFtZSIsIG5hbWUgPSAic3RhbmRhcmRfbmFtZSIsIHR5cGUgPSAiTkNfQ0hBUiIsIHZhbHVlID0gImJpb2xvZ2ljYWxfdGF4b25fbmFtZSIpDQphdHQucHV0Lm5jKG5jLCB2YXJpYWJsZSA9ICJ0YXhvbl9uYW1lIiwgbmFtZSA9ICJsb25nX25hbWUiLCB0eXBlID0gIk5DX0NIQVIiLCB2YWx1ZSA9ICJTY2llbnRpZmljIG5hbWUgb2YgdGhlIHRheGEiKQ0KDQojIFB1dCB0YXhvbl9uYW1lIGRhdGENCnZhci5wdXQubmMobmMsIHZhcmlhYmxlID0gInRheG9uX25hbWUiLCBkYXRhID0gdGF4b24kdGF4b25fbmFtZSkNCg0KIyBDaGVjaw0KdmFyLmdldC5uYyhuYywgdmFyaWFibGUgPSAidGF4b25fbmFtZSIpDQpgYGANCg0KDQojIyMjIHRheG9uX2xzaWQNCmBgYHtyIHRheG9uX2xzaWR9DQojIEFkZCB0YXhvbl9sc2lkIHZhcmlhYmxlIGFuZCBhdHRyaWJ1dGVzDQp2YXIuZGVmLm5jKG5jLCB2YXJuYW1lID0gInRheG9uX2xzaWQiLCB2YXJ0eXBlID0gIk5DX0NIQVIiLCBkaW1lbnNpb24gPSBjKCJzdHJpbmc4MCIsICJhcGhpYWlkIikpDQphdHQucHV0Lm5jKG5jLCB2YXJpYWJsZSA9ICJ0YXhvbl9sc2lkIiwgbmFtZSA9ICJzdGFuZGFyZF9uYW1lIiwgdHlwZSA9ICJOQ19DSEFSIiwgdmFsdWUgPSAiYmlvbG9naWNhbF90YXhvbl9sc2lkIikNCmF0dC5wdXQubmMobmMsIHZhcmlhYmxlID0gInRheG9uX2xzaWQiLCBuYW1lID0gImxvbmdfbmFtZSIsIHR5cGUgPSAiTkNfQ0hBUiIsIHZhbHVlID0gIkxpZmUgU2NpZW5jZSBJZGVudGlmaWVyIC0gV29ybGQgUmVnaXN0ZXIgb2YgTWFyaW5lIFNwZWNpZXMiKQ0KDQojIFB1dCB0YXhvbl9uYW1lIGRhdGENCnZhci5wdXQubmMobmMsIHZhcmlhYmxlID0gInRheG9uX2xzaWQiLCBkYXRhID0gdGF4b24kdGF4b25fbmFtZSkNCg0KIyBDaGVjaw0KdmFyLmdldC5uYyhuYywgdmFyaWFibGUgPSAidGF4b25fbHNpZCIpDQpgYGANCg0KDQojIyMgQ29vcmRpbmF0ZSBSZWZlcmVuY2UgU3lzdGVtDQoNCkEgbm9uLWRpbWVuc2lvbmFsIHZhcmlhYmxlIHdpbGwgYmUgZGVmaW5lZCB0byBob3N0IGFsbCB0aGUgaW5mbyBhYm91dCB0aGUgKipDb29yZGluYXRlIFJlZmVyZW5jZSBTeXN0ZW0gKENSUykqKi4gVGhpcyBpcyB1c2VmdWwgZm9yIHNvbWUgR0lTIHNvZnR3YXJlLg0KDQoqKkl0IGlzIGFzc3VtZWQgdGhhdCB0aGUgQ1JTIGlzIFdHUzg0KiouIElmIGl0IHdhcyBkaWZmZXJlbnQ6IHRyYW5zZm9ybSBgZGVjaW1hbExhdGl0dWRlYCBhbmQgYGRlY2ltYWxMb25naXR1ZGVgIHRvIFdHUzg0IGJlZm9yZSBoYW5kLiBTZWUgUiBwYWNrYWdlIGBzZmAuDQoNCmBgYHtyIGNyc30NCiMgRGVmaW5lIG5vbi1kaW1lbnNpb25hbCBjcnMgdmFyaWFibGUgDQp2YXIuZGVmLm5jKG5jLCB2YXJuYW1lID0gImNycyIsIHZhcnR5cGUgPSAiTkNfQ0hBUiIsIGRpbWVuc2lvbnMgPSBOQSkNCg0KIyBBZGQgYXR0cmlidXRlcw0KYXR0LnB1dC5uYyhuYywgdmFyaWFibGUgPSAiY3JzIiwgbmFtZSA9ICJsb25nX25hbWUiLCB0eXBlID0gIk5DX0NIQVIiLCB2YWx1ZSA9ICJDb29yZGluYXRlIFJlZmVyZW5jZSBTeXN0ZW0iKQ0KYXR0LnB1dC5uYyhuYywgdmFyaWFibGUgPSAiY3JzIiwgbmFtZSA9ICJnZW9ncmFwaGljX2Nyc19uYW1lIiwgdHlwZSA9ICJOQ19DSEFSIiwgdmFsdWUgPSAiV0dTIDg0IikNCmF0dC5wdXQubmMobmMsIHZhcmlhYmxlID0gImNycyIsIG5hbWUgPSAiZ3JpZF9tYXBwaW5nX25hbWUiLCB0eXBlID0gIk5DX0NIQVIiLCB2YWx1ZSA9ICJsYXRpdHVkZV9sb25naXR1ZGUiKQ0KYXR0LnB1dC5uYyhuYywgdmFyaWFibGUgPSAiY3JzIiwgbmFtZSA9ICJyZWZlcmVuY2VfZWxsaXBzb2lkX25hbWUiLCB0eXBlID0gIk5DX0NIQVIiLCB2YWx1ZSA9ICJXR1MgODQiKQ0KYXR0LnB1dC5uYyhuYywgdmFyaWFibGUgPSAiY3JzIiwgbmFtZSA9ICJob3Jpem9udGFsX2RhdHVtX25hbWUiLCB0eXBlID0gIk5DX0NIQVIiLCB2YWx1ZSA9ICJXR1MgODQiKQ0KYXR0LnB1dC5uYyhuYywgdmFyaWFibGUgPSAiY3JzIiwgbmFtZSA9ICJwcmltZV9tZXJpZGlhbl9uYW1lIiwgdHlwZSA9ICJOQ19DSEFSIiwgdmFsdWUgPSAiR3JlZW53aWNoIikNCmF0dC5wdXQubmMobmMsIHZhcmlhYmxlID0gImNycyIsIG5hbWUgPSAibG9uZ2l0dWRlX29mX3ByaW1lX21lcmlkaWFuIiwgdHlwZSA9ICJOQ19ET1VCTEUiLCB2YWx1ZSA9IDAuKQ0KYXR0LnB1dC5uYyhuYywgdmFyaWFibGUgPSAiY3JzIiwgbmFtZSA9ICJzZW1pX21ham9yX2F4aXMiLCB0eXBlID0gIk5DX0RPVUJMRSIsIHZhbHVlID0gNjM3ODEzNy4pDQphdHQucHV0Lm5jKG5jLCB2YXJpYWJsZSA9ICJjcnMiLCBuYW1lID0gInNlbWlfbWlub3JfYXhpcyIsIHR5cGUgPSAiTkNfRE9VQkxFIiwgdmFsdWUgPSA2MzU2NzUyLjMxNDI0NTE3OSkNCmF0dC5wdXQubmMobmMsIHZhcmlhYmxlID0gImNycyIsIG5hbWUgPSAiaW52ZXJzZV9mbGF0dGVuaW5nIiwgdHlwZSA9ICJOQ19ET1VCTEUiLCB2YWx1ZSA9IDI5OC4yNTcyMjM1NjMpDQphdHQucHV0Lm5jKG5jLCB2YXJpYWJsZSA9ICJjcnMiLCBuYW1lID0gInNwYXRpYWxfcmVmIiwgdHlwZSA9ICJOQ19DSEFSIiwgdmFsdWUgPSAnR0VPR0NTW1wiV0dTIDg0XCIsREFUVU1bXCJXR1NfMTk4NFwiLFNQSEVST0lEW1wiV0dTIDg0XCIsNjM3ODEzNywyOTguMjU3MjIzNTYzXV0sUFJJTUVNW1wiR3JlZW53aWNoXCIsMF0sVU5JVFtcImRlZ3JlZVwiLDAuMDE3NDUzMjkyNTE5OTQzMyxBVVRIT1JJVFlbXCJFUFNHXCIsXCI5MTIyXCJdXSxBWElTW1wiTGF0aXR1ZGVcIixOT1JUSF0sQVhJU1tcIkxvbmdpdHVkZVwiLEVBU1RdLEFVVEhPUklUWVtcIkVQU0dcIixcIjQzMjZcIl1dJykNCmF0dC5wdXQubmMobmMsIHZhcmlhYmxlID0gImNycyIsIG5hbWUgPSAiR2VvVHJhbnNmb3JtIiwgdHlwZSA9ICJOQ19DSEFSIiwgdmFsdWUgPSAnLTE4MCAwLjA4MzMzMzMzMzMzMzMzMzMzIDAgOTAgMCAtMC4wODMzMzMzMzMzMzMzMzMzMyAnKQ0KYGBgDQoNCg0KIyMgRGVmaW5lIHRoZSB2YXJpYWJsZSBvZiBpbnRlcmVzdA0KDQpUaGUgdmFyaWFibGUgdG8gYWRkIHRoZSBwcmVzZW5jZS9hYnNlbmNlIGRhdGEgKiptdXN0IGJlIGRlZmluZWQgYnkgdGhlIGZvdXIgZGltZW5zaW9ucyoqIGNvbnNpZGVyZWQgaW4gdGhpcyBkZW1vLiBUaGlzIGlzIHBhc3NlZCB0byBgdmFyLmRlZi5uYygpYCBpbiB0aGUgYXJndW1lbnQgYGRpbWVuc2lvbnNgIGFzICoqYSB2ZWN0b3IgY29udGFpbmluZyB0aGUgbmFtZXMgb2YgdGhlIGRpbWVuc2lvbnMuKioNCg0KVGhlIHZhbHVlcyBzdGF0aW5nIHByZXNlbmNlcyBhbmQgYWJzZW5jZXMgYXJlIDEgYW5kIDAsIGhlbmNlIHRoZSB2YXJpYWJsZSBtdXN0IGJlIG9mIHR5cGUgaW50ZWdlci4NCg0KU29tZSBvdGhlciBhdHRyaWJ1dGVzIGFyZSBhZGRlZC4gRS5nLiBgX0ZpbGxWYWx1ZWAgaXMgYSByZXF1aXJlbWVudCBmb3IgdGhlIENGLUNvbnZlbnRpb24gc3RhdGluZyB3aGF0IHZhbHVlIHdpbGwgYmUgdXNlZCBpbiBjYXNlIG9mIE5VTEwgb3IgTkEuIFRoaXMgaXMgdHlwaWNhbGx5IGAtOTk5OTlgLiBUaGUgYXR0cmlidXRlIGBsb25nX25hbWVgIGlzIGZyZWUgdGV4dCBhbmQgaXQgIGRlc2NyaWJlcyB0aGUgdmFyaWFibGUuDQoNCmBgYHtyIGRlZl9wYX0NCiMgQ3JlYXRlIHRoZSBwcmVzZW5jZV9hYnNlbmNlIHZhcmlhYmxlIGRlZmluZWQgYnkgdGhlIGZvdXIgZGltZW5zaW9ucw0KdmFyLmRlZi5uYyhuYywgdmFybmFtZSA9ICJwcmVzZW5jZV9hYnNlbmNlIiwgdmFydHlwZSA9ICJOQ19JTlQiLCBkaW1lbnNpb25zID0gYygibG9uIiwgImxhdCIsICJ0aW1lIiwgImFwaGlhaWQiKSkNCg0KIyBBZGQgYXR0cmlidXRlcw0KYXR0LnB1dC5uYyhuYywgdmFyaWFibGUgPSAicHJlc2VuY2VfYWJzZW5jZSIsIG5hbWUgPSAiX0ZpbGxWYWx1ZSIsIHR5cGUgPSAiTkNfSU5UIiwgdmFsdWUgPSAtOTk5OTkpDQphdHQucHV0Lm5jKG5jLCB2YXJpYWJsZSA9ICJwcmVzZW5jZV9hYnNlbmNlIiwgbmFtZSA9ICJsb25nX25hbWUiLCB0eXBlID0gIk5DX0NIQVIiLCB2YWx1ZSA9ICJQcm9iYWJpbGl0eSBvZiBvY2N1cnJlbmNlIG9mIGJpb2xvZ2ljYWwgZW50aXR5IikNCmBgYA0KDQpJZiB0aGVyZSB3ZXJlIGEgc3RhbmRhcmQgbmFtZSBmb3Igc3VjaCBhIHZhcmlhYmxlIGluIHRoZSBbQ0YtQ29udmVudGlvbl0oaHR0cDovL2NmY29udmVudGlvbnMub3JnLyksIHRoaXMgd291bGQgYmUgYWRkIHRvIGFuIGF0dHJpYnV0ZSBuYW1lZCBgc3RhbmRhcmRfbmFtZWAuDQoNCg0KIyMjIEFkZCBkYXRhIGZyb20gYSA0RCBBcnJheQ0KDQpBcyB3ZSBwcmV2aW91c2x5IGNyZWF0ZWQgYSA0RCBhcnJheSwgd2UgY2FuIHBhc3MgdGhpcyBkaXJlY3RseSB0byBgdmFyLnB1dC5uYygpYA0KDQpgYGB7ciBwdXRfYXJyYXl9DQp2YXIucHV0Lm5jKG5jLCB2YXJpYWJsZSA9ICJwcmVzZW5jZV9hYnNlbmNlIiwgZGF0YSA9IGFycmF5KSANCmBgYA0KDQoNCiMjIyBBZGQgZGF0YSBmcm9tIGEgdmVjdG9yOiBBbGwgYXQgb25jZQ0KDQpBZGRpbmcgdGhlIGRhdGEgZnJvbSBhIGRhdGEgc2V0IG9yIGEgdmVjdG9yIGlzIGFsc28gcG9zc2libGUgYnkgdXNpbmcgdGhlIHBhcmFtZXRlcnMgYHN0YXJ0YCBhbmQgYGNvdW50YCBpbiBgdmFyLnB1dC5uYygpYA0KKiogSW4gYHN0YXJ0YCwgZWFjaCBudW1iZXIgaW5kaWNhdGVzICoqdGhlIGluZGV4IGZyb20gd2hpY2ggeW91IHN0YXJ0IGFkZGluZyBkYXRhKiogZm9yIGVhY2ggdmFyaWFibGUuDQoqKiBJbiBgY291bnRgLCBlYWNoIG51bWJlciBpbmRpY2F0ZXMgKip0aGUgbGVuZ3RoIG9mIHRoZSBkYXRhIGFkZGVkKiogZm9yIGVhY2ggdmFyaWFibGUuDQoNCkluIHRoaXMgZGVtbywgYXMgd2UgaGF2ZSBhIDREIHZhcmlhYmxlLCB0aGUgdmVjdG9ycyB3aWxsIGJlIG9mIGxlbmd0aCA0LiBXZSBjYW4gYWRkIGFsbCB0aGUgZGF0YSBhdCBvbmNlICoqYnkgc2V0dGluZyBlYWNoIHN0YXJ0IGluZGV4IGFzIDEsIGFuZCBjb3VudCBhcyB0aGUgbGVuZ3RoIG9mIGVhY2ggZGltZW5zaW9uKiouIFJlbWVtYmVyIHRoZSBsZW5ndGggb2YgbG9uIGlzIDEwLCBsYXQgaXMgMTAsIHRpbWUgaXMgMyBhbmR0YXhvbiBpcyA0LiBgc3RhcnRgIGlzIHNldCBhcyBgYygxLCAxLCAxLCAxKWAgYXMgd2UgYWRkIGFsbCB0aGUgZGF0ZSBmcm9tIHRoZSBiZWdpbm5pbmcgb2YgZWFjaCBkaW1lbnNpb24uDQoNCmBgYHtyIHB1dF9hbGxfYXRfb25jZX0NCnZhci5wdXQubmMobmMsIHZhcmlhYmxlID0gInByZXNlbmNlX2Fic2VuY2UiLCANCiAgICAgICAgICAgZGF0YSA9IGxvbmdlciRvY2N1cnJlbmNlU3RhdHVzLCANCiAgICAgICAgICAgc3RhcnQgPSBjKDEsIDEsIDEsIDEpLCANCiAgICAgICAgICAgY291bnQgPSBjKDEwLCAxMCwgMywgNCkpIA0KYGBgDQoNCg0KIyMjIEFkZCBkYXRhIGZyb20gYSB2ZWN0b3I6IEluIGNodW5rcw0KDQpIb3dldmVyLCBvdXQgaW4gdGhlIHdpbGQgdGhlIGRhdGEgcHJvZHVjdCB3aWxsIGJlY29tZSB2ZXJ5IGxhcmdlIHdoZW4gYWRkaW5nIGFsbCBwb3NzaWJsZSBjb21iaW5hdGlvbnMgb2YgdGhlIGRpbWVuc2lvbnMuIFNlZSBob3cgdGhlIGRlbW8gZGF0YSBzZXQgb2Ygb25seSAxMCByb3dzIGV4cGxvZGVkIHVwIHRvIDEyMDAgcm93cyB3aGVuIGFkZGluZyBhbGwgcG9zc2libGUgY29tYmluYXRpb25zIG9mIHRoZSBmb3VyIGRpbWVuc2lvbnMNCg0KYGBge3IgY29tcGFyZV9zaXplfQ0KbnJvdyhkYXRhc2V0KQ0KDQpucm93KGxvbmdlcikNCmBgYA0KDQpJbWFnaW5lIHdoYXQgd291bGQgaGFwcGVuIHdpdGggYSByZWFsIGRhdGEgc2V0IHdpdGggdGhvdXNhbmRzIG9mIHJvd3MhDQoNClR5cGljYWxseSB5b3Ugd29uJ3QgaGF2ZSBlbm91Z2ggKiptZW1vcnkqKiBpbiB5b3VyIGNvbXB1dGVyIHRvICoqY3JlYXRlIHN1Y2ggYSBsYXJnZSBhcnJheSoqLiBUbyB3b3JrIGFyb3VuZCB0aGlzIGlzc3VlLCB5b3UgY2FuICoqYWRkIGRhdGEgaW4gY2h1bmtzKiogYnkgc2V0dGluZyBjb3JyZWN0bHkgYHN0YXJ0YCBhbmQgYGNvdW50YA0KDQpGb3IgaW5zdGFuY2UsIHlvdSBjYW4gKipsb29wIG92ZXIqKiB0aGUgZGF0YSB0byAqKmFkZCBvbmUgdGF4b24gcGVyIGl0ZXJhdGlvbioqLiANCg0KYGBge3IgcHV0X2J5X3RheG9ufQ0KIyBJdGVyYXRlIGZyb20gMSB0byA0IGFzIHRoZXJlIGFyZSA0IHBvc3NpYmxlIHRheG9ucw0KZm9yKGkgaW4gMTo0KXsNCiAgDQogIHRheGFfaSA8LSB0YXhvbiRhcGhpYWlkW2ldDQogIA0KICBkYXRhX2F0X3RheGFfaSA8LSBzdWJzZXQobG9uZ2VyLCBsb25nZXIkYXBoaWFpZCA9PSB0YXhhX2kpDQogIA0KICB2YXIucHV0Lm5jKG5jLCB2YXJpYWJsZSA9ICJwcmVzZW5jZV9hYnNlbmNlIiwgZGF0YSA9IGRhdGFfYXRfdGF4YV9pJG9jY3VycmVuY2VTdGF0dXMsIHN0YXJ0ID0gYygxLCAxLCAxLCBpKSwgY291bnQgPSBjKDEwLCAxMCwgMywgMSkpIA0KICANCn0NCmBgYA0KDQpOb3RlIHRoYXQgdGhlIGxhc3QgZWxlbWVudCBvZiBzdGFydCBpcyBzZXQgYXMgYGlgLCB3aGljaCBpcyB0aGUgbGVuZ3RoIG9mIHRoZSBkaW1lbnNpb24gYGFwaGlhaWRgOiAxIHRvIDQNCg0KVGhlIGxhc3QgZWxlbWVudCBvZiBgY291bnRgIGNvcnJlc3BvbmRzIHRvIHRoZSBjb3VudCBvZiBgYXBoaWFpZC5gIEl0IGlzIHNldCBhcyAxIGJlY2F1c2UgaW4gZWFjaCBpdGVyYXRpb24gb25seSB0aGUgZGF0YSBvZiBvbmUgdGF4b24gaXMgcHV0IGluIHRoZSB2YXJpYWJsZS4NCg0KDQojIyMgQWRkIGRhdGEgZnJvbSBhIHZlY3RvcjogT25lIGJ5IG9uZQ0KDQpGdXJ0aGVybW9yZSwgeW91IGNhbiAqKm5lc3QgbG9vcHMqKiBhcyBtdWNoIGFzIHlvdSB3YW50IHRvICoqYWRkIHRoZSBzbGljZSBvZiBkYXRhIHRoYXQgeW91IGNhbiBoYW5kbGUgYXQgb25jZSoqLiBCZWxvdyB0aGVyZSBpcyBhbiAqKmV4dHJlbWUgZXhhbXBsZSoqIG9mIGEgZm91ciB0aW1lcyBuZXN0ZWQgbG9vcCwgaW4gd2hpY2ggKippbiBlYWNoIGl0ZXJhdGlvbiBvbmx5IG9uZSBkYXRhIGlzIGFkZGVkKiogdG8gdGhlIE5ldENERiBmaWxlLg0KDQpSZW1lbWJlciB0byB1c2UgdGhlIGxlbmd0aCBvZiBlYWNoIGRpbWVuc2lvbiBpbiB0aGUgYGZvcigpYCBsb29wLiBOb3RlIHRoYXQgYGNvdW50YCBpcyBzZXQgYXMgYGMoMSwgMSwgMSwgMSlgIGFzIG9ubHkgb25lIGRhdGEgcG9pbnQgaXMgYWRkZWQgaW4gZWFjaCBpdGVyYXRpb24uIFRoZSBhcmd1bWVudCBgc3RhcnRgIGNvbnRhaW5zIHRoZSBpbmRleCBvZiBlYWNoIGxvb3AuDQoNCmBgYHtyIHB1dF80dGltZXNfbmVzdGVkX2xvb3AsIGV2YWw9RkFMU0V9DQpmb3IobG9uaSBpbiAxOjEwKXsNCiAgZm9yKGxhdGkgaW4gMToxMCl7DQogICAgZm9yKHRpbWVpIGluIDE6Myl7DQogICAgICBmb3IodGF4YWkgaW4gMTo0KXsNCiAgICAgICAgDQogICAgICAgICMgUmVhZCBvbmx5IG9uZSByb3cgcGVyIGl0ZXJhdGlvbg0KICAgICAgICBkYXRhX2F0X2kgPC0gbG9uZ2VyICU+JQ0KICAgICAgICAgIGZpbHRlcigNCiAgICAgICAgICAgIGFwaGlhaWQgPT0gdGF4b24kYXBoaWFpZFt0YXhhaV0sDQogICAgICAgICAgICB0aW1lID09IHRpbWVbdGltZWldLA0KICAgICAgICAgICAgbGF0ID09IGxhdFtsYXRpXSwNCiAgICAgICAgICAgIGxvbiA9PSBsb25bbG9uaV0NCiAgICAgICAgICApDQogICAgICAgIA0KICAgICAgICAjIFB1dCB0aGUgZGF0YSBpbnRvIA0KICAgICAgICB2YXIucHV0Lm5jKG5jLCB2YXJpYWJsZSA9ICJwcmVzZW5jZV9hYnNlbmNlIiwgDQogICAgICAgICAgICAgICAgICAgZGF0YSA9IGRhdGFfYXRfaSRvY2N1cnJlbmNlU3RhdHVzLCANCiAgICAgICAgICAgICAgICAgICBzdGFydCA9IGMobG9uaSwgbGF0aSwgdGltZWksIHRheGFpKSwgDQogICAgICAgICAgICAgICAgICAgY291bnQgPSBjKDEsIDEsIDEsIDEpKSANCiAgICAgIH0NCiAgICB9DQogIH0NCn0NCmBgYA0KDQpOb3RlIHRoaXMgaXMgYW4gZXh0cmVtZSBleGFtcGxlIGFuZCBpdHMgcGVyZm9ybWFuY2UgaXMgbm90IG9wdGltYWwuDQoNCg0KIyMjIEFkZCBkYXRhIGZyb20gZGlzaw0KDQpUaGVzZSBsb29wcyBhcmUgc3BlY2lhbGx5IHVzZWZ1bCB0byB0dXJuIGxhcmdlIGFtb3VudHMgb2YgZGF0YSBpbnRvIE5ldENERiBhcyAqKnlvdSBjYW4gaGF2ZSB5b3VyIGRhdGEgc2F2ZWQgaW50byBkaXNrIGFuZCBpdGVyYXRlIHRvIHJlYWQgYSBjZXJ0YWluIHBhcnQgb2YgaXQgYW5kIHB1dCBpbnRvIG5ldGNkZiBhdCBhIHRpbWUuKioNCg0KSW4gbmV4dCBhbmQgbGFzdCBleGFtcGxlLCBpdCBpcyAqKmFzc3VtZWQgdGhhdCB0aGUgZGF0YSBpcyB0b28gbGFyZ2UgdG8gYmUgcmVhZCBpbnRvIG1lbW9yeSoqLiBJbiB0aGlzIGNhc2UsIHdlIGNhbiB3cml0ZSAqKmEgbG9vcCB0aGF0IHJlYWRzIGEgY2h1bmsgb2YgZGF0YSoqIHRoYXQgd2Uga25vdyB0aGF0IFIgY2FuIGhhbmRsZSwgKippbm1lZGlhdGVseSBwdXQgaXQgaW4gdGhlIE5ldENERiBmaWxlKiosIGFuZCBvbmNlIGl0IGlzIHNhdmVkIHRoZXJlIGFzIE5ldENERiBzaW1wbHkgcmVtb3ZlIGZyb20gbWVtb3J5Lg0KDQpZb3UgY2FuIHJlYWQgY2h1bmtzIG9mIGRhdGEgZnJvbSBhIGAuY3N2YCBmaWxlIGVpdGhlciB3aXRoIGByZWFkcjo6cmVhZF9jc3ZfY2h1bmtlZGAgb3IgYHNxbGRmOjpyZWFkLmNzdi5zcWxgLiBXZSB3aWxsIHVzZSB0aGUgZmlyc3QgaW4gdGhpcyBleGFtcGxlLiBSZW1lbWJlciB3ZSBzYXZlZCB0aGUgYGxvbmdlcmAgZGF0YSBmcmFtZSBpbnRvIGAuL2RhdGEvZGVyaXZlZC9sb25nZXIuY3N2YC4NCg0KYGBge3IgcHV0X2NobnVua3MsIG1lc3NhZ2U9RkFMU0V9DQpmb3IoaSBpbiAxOjQpew0KICANCiAgIyBJZGVudGlmeSB0aGUgdGF4YSBvZiBpbnRlcmVzdCBiYXNlZCBvbiB0aGUgaSBpbmRleA0KICB0YXhhX2kgPC0gdGF4b24kYXBoaWFpZFtpXQ0KICANCiAgIyBSZWFkIGEgY2h1bmsgb2YgZGF0YSwgc3Vic2V0dGluZyB0byBvbmx5IHRoZSB0YXhhIG9mIGludGVyZXN0IGluIGVhY2ggaXRlcmF0aW9uDQogIGRhdGFfYXRfdGF4YV9pIDwtIHJlYWRfY3N2X2NodW5rZWQoDQogICAgZmlsZSA9ICcuL2RhdGEvZGVyaXZlZC9sb25nZXIuY3N2JywgDQogICAgY2FsbGJhY2sgPSBEYXRhRnJhbWVDYWxsYmFjayRuZXcoDQogICAgICBmdW5jdGlvbih4LCBwb3MpIHN1YnNldCh4LCBhcGhpYWlkID09IHRheGFfaSkNCiAgICApDQogICkNCiAgDQogICMgUHV0IHRoZSBkYXRhIGludG8gdGhlIE5ldENERiBmaWxlDQogIHZhci5wdXQubmMobmMsIHZhcmlhYmxlID0gInByZXNlbmNlX2Fic2VuY2UiLCANCiAgICAgICAgICAgICBkYXRhID0gZGF0YV9hdF90YXhhX2kkb2NjdXJyZW5jZVN0YXR1cywgDQogICAgICAgICAgICAgc3RhcnQgPSBjKDEsIDEsIDEsIGkpLCANCiAgICAgICAgICAgICBjb3VudCA9IGMoMTAsIDEwLCAzLCAxKSkgDQogIA0KICAjIElubWVkaWF0ZWx5IHJlbW92ZSB0aGUgZGF0YXNldCByZWFkIGluIHRoZSBpdGVyYXRpb24gdG8gZnJlZSB1cCBtZW1vcnkNCiAgcm0oZGF0YV9hdF90YXhhX2kpDQp9DQpgYGANCg0KDQojIyBHbG9iYWwgQXR0cmlidXRlcw0KDQpOZXRDREYgZmlsZXMgY2FuIGhvc3QgKiptZXRhZGF0YSBpbiB0aGUgZm9ybSBvZiBnbG9iYWwgYXR0cmlidXRlcyoqLiBUaGVzZSBjb250YWluIGluZm9ybWF0aW9uIHN1Y2ggYXMgdGhlIGF1dGhvciwgZGF0YSBvZiBjcmVhdGlvbiwgYWZmaWxpYXRpb24sIGNpdGF0aW9uIG9yIGxpY2Vuc2UuIA0KDQpTZWUgYmVsb3cgYW4gZXhhbXBsZSBvZiBob3cgZGVmaW5pbmcgYSBudW1iZXIgb2YgZ2xvYmFsIGF0dHJpYnV0ZXMgYW5kIGFkZGluZyB0aGVtIHByb2dyYW1hdGljYWxseS4NCg0KKipDbGljayBvbiBgY29kZWAgdG8gdW5mb2xkKioNCmBgYHtyIGdsb2JhbF9hdHQsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQ0KYXR0cmlidXRlcyA8LSBsaXN0KA0KICB0aXRsZSA9ICJFeGFtcGxlIHRpdGxlIiwNCiAgc3VtbWFyeSA9ICJUaGlzIGlzIHRoZSByZXN1bHQgb2YgYSBkZW1vIHRvIHNob3cgaG93IHRvIHR1cm4gYSB2YXJpYWJsZSBkZWZpbmVkIGJ5IGZvdXIgZGltZW5zaW9ucyBpbnRvIGEgbmV0Y2RmIGFycmF5LiIsICAgICAgICAgICAgICAgICAgICAgICANCiAgQ29udmVudGlvbnMgPSAiQ0YtMS44IiwNCiAgIyBpZCA9ICIiLA0KICBuYW1pbmdfYXV0aG9yaXR5ID0gImVtb2RuZXQtYmlvbG9neS5ldSIsDQogIGhpc3RvcnkgPSAiaHR0cHM6Ly9naXRodWIuY29tL0VNT0RuZXQvRU1PRG5ldC1CaW9sb2d5LXByb2R1Y3RzLWVyZGRhcC1kZW1vIiwNCiAgc291cmNlID0gImh0dHBzOi8vZ2l0aHViLmNvbS9FTU9EbmV0L0VNT0RuZXQtQmlvbG9neS1wcm9kdWN0cy1lcmRkYXAtZGVtbyIsDQogICMgcHJvY2Vzc2luZ19sZXZlbCA9ICIiLA0KICAjIGNvbW1lbnQgPSAiIiwgDQogICMgYWNrbm93bGVkZ21lbnQgPSAiIiwNCiAgbGljZW5zZSA9ICJDQy1CWSIsDQogIHN0YW5kYXJkX25hbWVfdm9jYWJ1bGFyeSA9ICJDRiBTdGFuZGFyZCBOYW1lIFRhYmxlIHYxLjgiLA0KICBkYXRlX2NyZWF0ZWQgPSBhcy5jaGFyYWN0ZXIoU3lzLkRhdGUoKSksDQogIGNyZWF0b3JfbmFtZSA9ICJTYWx2YWRvciBGZXJuYW5kZXoiLA0KICBjcmVhdG9yX2VtYWlsID0gInNhbHZhZG9yLmZlcm5hbmRlekB2bGl6LmJlIiwNCiAgY3JlYXRvcl91cmwgPSAid3d3LnZsaXouYmUiLA0KICBpbnN0aXR1dGlvbiA9ICJGbGFuZGVycyBNYXJpbmUgSW5zdGl0dXRlIChWTElaKSIsDQogIHByb2plY3QgPSAiRU1PRG5ldC1CaW9sb2d5IiwNCiAgcHVibGlzaGVyX25hbWUgPSAiRU1PRG5ldC1CaW9sb2d5IiwgICAgICAgICAgICAgICAgIA0KICBwdWJsaXNoZXJfZW1haWwgPSAiYmlvQGVtb2RuZXQuZXUiLCAgICAgICAgICAgICAgICANCiAgcHVibGlzaGVyX3VybCA9ICJ3d3cuZW1vZG5ldC1iaW9sb2d5LmV1IiwgICAgICAgICAgICAgICAgICANCiAgIyBnZW9zcGF0aWFsX2JvdW5kcyA9ICIiLCAgICAgICAgICAgICAgDQogICMgZ2Vvc3BhdGlhbF9ib3VuZHNfY3JzID0gIiIsICAgICAgICAgIA0KICAjIGdlb3NwYXRpYWxfYm91bmRzX3ZlcnRpY2FsX2NycyA9ICIiLCANCiAgZ2Vvc3BhdGlhbF9sYXRfbWluID0gbWluKGxhdCksDQogIGdlb3NwYXRpYWxfbGF0X21heCA9IG1heChsYXQpLA0KICBnZW9zcGF0aWFsX2xvbl9taW4gPSBtaW4obG9uKSwNCiAgZ2Vvc3BhdGlhbF9sb25fbWF4ID0gbWF4KGxvbiksDQogICMgZ2Vvc3BhdGlhbF92ZXJ0aWNhbF9taW4gPSAiIiwgICAgICAgIA0KICAjIGdlb3NwYXRpYWxfdmVydGljYWxfbWF4ID0gIiIsICAgICAgICANCiAgIyBnZW9zcGF0aWFsX3ZlcnRpY2FsX3Bvc2l0aXZlID0gIiIsICANCiAgIyB0aW1lX2NvdmVyYWdlX3N0YXJ0ID0gIjE5MTEiLCAgICAgICAgICAgIA0KICAjIHRpbWVfY292ZXJhZ2VfZW5kID0gIjIwMTYiLCAgICAgICAgICAgICAgDQogICMgdGltZV9jb3ZlcmFnZV9kdXJhdGlvbiA9ICIiLCAgICAgICAgIA0KICAjIHRpbWVfY292ZXJhZ2VfcmVzb2x1dGlvbiA9ICIiLCAgICAgICANCiAgIyB1dWlkID0gIiIsICAgICAgICAgICAgICAgICAgICAgICAgICAgDQogICMgc2VhX25hbWUgPSAiIiwgICAgICAgICAgICAgICAgICAgICAgIA0KICAjIGNyZWF0b3JfdHlwZSA9ICIiLCAgICAgICAgICAgICAgICAgICANCiAgY3JlYXRvcl9pbnN0aXR1dGlvbiA9ICJGbGFuZGVycyBNYXJpbmUgSW5zdGl0dXRlIChWTElaKSIsICAgICAgICAgICAgDQogICMgcHVibGlzaGVyX3R5cGUgPSAiIiwgICAgICAgICAgICAgICAgIA0KICBwdWJsaXNoZXJfaW5zdGl0dXRpb24gPSAiRmxhbmRlcnMgTWFyaW5lIEluc3RpdHV0ZSAoVkxJWikiLCAgICAgICAgDQogICMgcHJvZ3JhbSA9ICIiLCAgICAgICAgICAgICAgICAgICAgICAgIA0KICAjIGNvbnRyaWJ1dG9yX25hbWUgPSAiIiwgICAgICAgICAgICAgICANCiAgIyBjb250cmlidXRvcl9yb2xlICA9ICIiLCAgICAgICAgICAgICAgDQogIGdlb3NwYXRpYWxfbGF0X3VuaXRzID0gImRlZ3JlZXNfbm9ydGgiLCAgICAgICAgICAgDQogIGdlb3NwYXRpYWxfbG9uX3VuaXRzID0gImRlZ3JlZXNfZWFzdCIsICAgICAgICAgICANCiAgIyBnZW9zcGF0aWFsX3ZlcnRpY2FsX3VuaXRzICAgPSAiIiwgICAgDQogICMgZGF0ZV9tb2RpZmllZCA9ICIiLCAgICAgICAgICAgICAgIA0KICAjIGRhdGVfaXNzdWVkID0gIiIsICAgICAgICAgICAgICAgICAgICANCiAgIyBkYXRlX21ldGFkYXRhX21vZGlmaWVkICAgPSAiIiwgICAgICAgDQogICMgcHJvZHVjdF92ZXJzaW9uID0gIiIsICAgICAgICAgICAgDQogICMga2V5d29yZHNfdm9jYWJ1bGFyeSA9ICIiLCAgICAgICAgICANCiAgIyBwbGF0Zm9ybSAgPSAiIiwgICAgICAgICAgICAgIA0KICAjIHBsYXRmb3JtX3ZvY2FidWxhcnkgPSAiIiwgICAgICAgICAgDQogICMgaW5zdHJ1bWVudCA9ICIiLCAgICAgICAgICANCiAgIyBpbnN0cnVtZW50X3ZvY2FidWxhcnkgID0gIiIsICAgICAgICANCiAgIyBmZWF0dXJlVHlwZSA9ICJQb2ludCIsICAgICAgICAgICAgICAgICAgDQogICMgbWV0YWRhdGFfbGluayA9ICIiLCAgICAgICAgICAgICAgICAgIA0KICAjIHJlZmVyZW5jZXMgPSAiIiwNCiAgY29tbWVudCA9ICJVc2VzIGF0dHJpYnV0ZXMgcmVjb21tZW5kZWQgYnkgaHR0cDovL2NmY29udmVudGlvbnMub3JnIiwNCiAgbGljZW5zZSA9ICJDQy1CWSIsIA0KICBwdWJsaXNoZXJfbmFtZSA9ICJFTU9EbmV0IEJpb2xvZ3kgRGF0YSBNYW5hZ2VtZW50IFRlYW0iLA0KICBjaXRhdGlvbiA9ICJGZXJuw6FuZGV6LUJlamFyYW5vLCBTYWx2YWRvci4gMjAyMi4gQ3JlYXRlIGEgRU1PRG5ldC1CaW9sb2d5IGRhdGEgcHJvZHVjdCBhcyBOZXRDREYuIiwNCiAgYWNrbm93bGVkZ2VtZW50ID0gIkV1cm9wZWFuIE1hcmluZSBPYnNlcnZhdGlvbiBEYXRhIE5ldHdvcmsgKEVNT0RuZXQpIEJpb2xvZ3kgcHJvamVjdCAoRU1GRi8yMDE5LzEuMy4xLjkvTG90IDYvU0kyLjgzNzk3NCksIGZ1bmRlZCBieSB0aGUgRXVyb3BlYW4gVW5pb24gdW5kZXIgUmVndWxhdGlvbiAoRVUpIE5vIDUwOC8yMDE0IG9mIHRoZSBFdXJvcGVhbiBQYXJsaWFtZW50IGFuZCBvZiB0aGUgQ291bmNpbCBvZiAxNSBNYXkgMjAxNCBvbiB0aGUgRXVyb3BlYW4gTWFyaXRpbWUgYW5kIEZpc2hlcmllcyBGdW5kIg0KKQ0KDQojIERlZmluZSBmdW5jdGlvbiB0aGF0IGRldGVjdHMgaWYgdGhlIGRhdGEgdHlwZSBzaG91bGQgYmUgY2hhcmFjdGVyIG9mIA0KIyBpbnRlZ2VyIGFuZCBhZGQgdG8gZ2xvYmFsIGF0dHJpYnV0ZXMNCmFkZF9nbG9iYWxfYXR0cmlidXRlcyA8LSBmdW5jdGlvbihuYywgYXR0cmlidXRlcyl7DQogIA0KICBzdG9waWZub3QoaXMubGlzdChhdHRyaWJ1dGVzKSkNCiAgDQogIGZvcihpIGluIDE6bGVuZ3RoKGF0dHJpYnV0ZXMpKXsNCiAgICBpZihpcy5jaGFyYWN0ZXIoYXR0cmlidXRlc1tbaV1dKSl7DQogICAgICB0eXBlIDwtICJOQ19DSEFSIg0KICAgIH1lbHNlIGlmKGlzLm51bWVyaWMoYXR0cmlidXRlc1tbaV1dKSl7DQogICAgICB0eXBlIDwtICJOQ19ET1VCTEUiDQogICAgfQ0KICAgIGF0dC5wdXQubmMobmMsIHZhcmlhYmxlID0gIk5DX0dMT0JBTCIsIG5hbWUgPSBuYW1lcyhhdHRyaWJ1dGVzW2ldKSwgdHlwZSA9IHR5cGUsIHZhbHVlID0gYXR0cmlidXRlc1tbaV1dKQ0KICB9DQogIHN5bmMubmMobmMpDQp9DQoNCiMgQWRkIGF0dHJpYnV0ZXMNCmFkZF9nbG9iYWxfYXR0cmlidXRlcyhuYywgYXR0cmlidXRlcykNCmBgYA0KDQoNCiMjIFdyYXBwaW5nIHVwDQoNCkNvbmdyYXR1bGF0aW9ucyEgVGhlIE5ldENERiBmaWxlIGNvbnRhaW5pbmcgdGhlIHByb2R1Y3QgaGFzIGJlZW4gY3JlYXRlZC4gTm93IHlvdSBjYW4gaW5zcGVjdCB0aGUgZmlsZS4NCg0KYGBge3IgaW5zcGVjdH0NCnN5bmMubmMobmMpDQpwcmludC5uYyhuYykNCmBgYA0KDQpZb3UgY2FuIHJlYWQgdGhlIHZhcmlhYmxlcyB5b3UgaGF2ZSBjcmVhdGVkIHdpdGggYHZhci5nZXQubmNgDQoNCmBgYHtyIHJlYWRfdmFyc30NCiMgR2V0IHZhcmlhYmxlLCB0dXJuIGFycmF5IGludG8gdmVjdG9yIGFuZCBnZXQgdW5pcXVlIHZhbHVlcw0KdW5pcXVlKGModmFyLmdldC5uYyhuYywgdmFyaWFibGUgPSAicHJlc2VuY2VfYWJzZW5jZSIpKSkNCmBgYA0KDQpJZiBldmVyeXRoaW5nIGlzIGNvcnJlY3QsIGNsb3NlIHRoZSBmaWxlLiBSZW1lbWJlciBpdCB3YXMgc2F2ZWQgaW50byBgLi9kYXRhL2Rlcml2ZWQvZm9vLm5jYA0KDQpgYGB7ciBjbG9zZX0NCmNsb3NlLm5jKG5jKQ0KYGBgDQoNCg==