Installing fwsinat

The fwsinat package requires you to have R (>= 3.3) installed on your computer as well as Rtools. Both will require administrative priveleges but the installation of packages after this initial install will not.

With R and Rtools installed, it’s simple to install and load the fwsinat package to access its functionality.

NOTE: If you receive a SSL or CA Certificate error, you may need to take the extra step documented below.

# If devtools package is not installed
install.packages("devtools", dependencies = TRUE)

# Now install and load fwsinat
devtools::install_github("adamdsmith/fwsinat")
library("fwsinat")

# If you receive a SSL or CA Certificate error
install.packages("httr")
library("httr")
set_config(config(ssl_verifypeer = 0L))
devtools::install_github("adamdsmith/fwsinat")
library("fwsinat")

The fwsinat package

This packages currently contains functions to:

  1. retrieve iNaturalist observations for any number of available USFWS properties (inat_retrieve), the valid names of which can be obtained with the find_refuges function;
  2. update previous retrievals of observations (inat_update);
  3. export the observations to separate spreadsheets by USFWS property for distribution (inat_export); and
  4. harvest observations on USFWS properties into the USFWS NWRS iNaturalist project (inat_harvest)

Using fwsinat

The first step to using fwsinat is to generate a list of USFWS properties from which you’d like to retrieve observations. This can be done using the find_refuges function, which accepts as input a vector of strings you expect will return your desired refuge(s). The find_refuges function is case-insensitive and works with partial matches. For example, if we’re interested in retrieving observations on Harris Neck, Loxahatchee, and Chassahowitzka NWRs, we can input a vector of strings we think are adequate to return those refuges:

refs <- c("merritt", "lox", "chass")
(refs <- find_refuges(refs))

Success! Note that some searches will return multiple matches that you may not be seeking. If we’re interested in Hatchie NWR, for example:

(hatchie <- find_refuges("hatchie"))

Three matches! No worries! We can either select the one we want ad hoc or use regular expressions to narrow the matches:

## Narrow the search after the fact
(hatchie <- hatchie[1])

# Narrow the search with regular expressions
# In this case, require the property to start with "Hatchie"
(hatchie <- find_refuges("^hatchie"))

There are a couple more options to the find_refuges function to help you narrow your search; use ?find_refuges for more information. By default, find_refuges returns ALL available USFWS properties.

Now let’s pick a refuge and retrieve the observations from iNaturalist… inat_retrieve is our friend. Before we do so, however, we need to decide on a few options:

  1. Do we want all iNaturalist observations on the property or only those from a specific project?

    By default, inat_retrieve returns only observations associated with the USFWS National Wildlife Refuge System iNaturalist project. If you want a different project, you’ll need to specify which one with the inat_proj argument (see ?inat_retrieve for guidance on what’s expected). If you want ALL observations, use inat_proj = NULL (see below).

  2. Do we want to limit the dates of those observations?

    By default, inat_retrieve does not limit retrievals by date range. See ?inat_retrieve for guidance if this is of interest.

  3. Do we want helpful messages during the retrieval?

    Generally these are useful to track progress, so the default is to print these messages. If they annoy you, you can reduce them to a minimum by passing verbose = FALSE to inat_retrieve.

Example: Harris Neck NWR

hn <- find_refuges("harris neck")
# Harris Neck records ONLY on USFWS NWRS iNaturalist Project
hn_recs <- inat_retrieve(hn)
Processing Harris Neck National Wildlife Refuge within the
usfws-national-wildlife-refuge-system project.
Retrieving 32 records.
Records retrieved: 
  0-32
# Harris Neck records across ALL iNaturalist Projects
hn_all <- inat_retrieve(hn, inat_proj = NULL)
Processing Harris Neck National Wildlife Refuge across all iNaturalist projects.
Retrieving 55 records.
Records retrieved: 
  0-55

Notice there were 23 observations on the refuge that do not belong to the USFWS NWRS project. We want those observations! We can try and harvest them with inat_harvest.

This will require you to have an iNaturalist account and to pass your username and password. I illustrate it here while keeping my credentials hidden, but you can pass your username and password as character strings to the user and pw arguments of the function…

hn_harvest <- inat_harvest(hn, user = Sys.getenv("user"), pw = Sys.getenv("pw"), interactive = FALSE)
23 observations available for harvest on Harris Neck National Wildlife Refuge.

   |                                                  | 0 % ~calculating  
   |+++                                               | 4 % ~28s          
   |+++++                                             | 9 % ~29s          
   |+++++++                                           | 13% ~26s          
   |+++++++++                                         | 17% ~25s          
   |+++++++++++                                       | 22% ~23s          
   |++++++++++++++                                    | 26% ~22s          
   |++++++++++++++++                                  | 30% ~21s          
   |++++++++++++++++++                                | 35% ~20s          
   |++++++++++++++++++++                              | 39% ~18s          
   |++++++++++++++++++++++                            | 43% ~17s          
   |++++++++++++++++++++++++                          | 48% ~15s          
   |+++++++++++++++++++++++++++                       | 52% ~14s          
   |+++++++++++++++++++++++++++++                     | 57% ~13s          
   |+++++++++++++++++++++++++++++++                   | 61% ~11s          
   |+++++++++++++++++++++++++++++++++                 | 65% ~10s          
   |+++++++++++++++++++++++++++++++++++               | 70% ~09s          
   |+++++++++++++++++++++++++++++++++++++             | 74% ~08s          
   |++++++++++++++++++++++++++++++++++++++++          | 78% ~06s          
   |++++++++++++++++++++++++++++++++++++++++++        | 83% ~05s          
   |++++++++++++++++++++++++++++++++++++++++++++      | 87% ~04s          
   |++++++++++++++++++++++++++++++++++++++++++++++    | 91% ~03s          
   |++++++++++++++++++++++++++++++++++++++++++++++++  | 96% ~01s          
   |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed = 29s

Occasionally observations cannot be harvested because the user restricts their observations. If this happens, you will receive a message indicating how many records could not be harvested. In the case of Harris Neck, we do not receive such a message because all records were successfully harvested.

If some records cannot be harvested, however, you can explore the reasons for these failures by ‘tabling’ the error message column of the object you just created.

# This doesn't make sense for Harris Neck, but here's how you'd look at reasons any records
# were not harvested to the USFWS NWRS project...
# table(hn_harvest$error_msg)
# USFWS NWRS project now contains all currently harvestable observations
hn_recs <- inat_retrieve(hn)
Processing Harris Neck National Wildlife Refuge within the
usfws-national-wildlife-refuge-system project.
Retrieving 55 records.
Records retrieved: 
  0-55

Notice that all 55 records on Harris Neck are now in the USFWS NWRS project…

Try it yourself for Savannah NWR (126 records available for harvest when I last checked) or some other refuge… Note that record harvesting takes between 1-2 seconds per record, so plan accordingly for large harvests.

sav <- find_refuges("savannah")
LS0tDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIEluc3RhbGxpbmcgYGZ3c2luYXRgDQoNClRoZSBgZndzaW5hdGAgcGFja2FnZSByZXF1aXJlcyB5b3UgdG8gaGF2ZSBbUl0oaHR0cHM6Ly93d3cuci1wcm9qZWN0Lm9yZy8pICg+PSAzLjMpIGluc3RhbGxlZCBvbiB5b3VyIGNvbXB1dGVyIGFzIHdlbGwgYXMgW1J0b29sc10oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvYmluL3dpbmRvd3MvUnRvb2xzLykuICBCb3RoIHdpbGwgcmVxdWlyZSBhZG1pbmlzdHJhdGl2ZSBwcml2ZWxlZ2VzIGJ1dCB0aGUgaW5zdGFsbGF0aW9uIG9mIHBhY2thZ2VzIGFmdGVyIHRoaXMgaW5pdGlhbCBpbnN0YWxsIHdpbGwgbm90Lg0KDQpXaXRoIFIgYW5kIFJ0b29scyBpbnN0YWxsZWQsIGl0J3Mgc2ltcGxlIHRvIGluc3RhbGwgYW5kIGxvYWQgdGhlIGBmd3NpbmF0YCBwYWNrYWdlIHRvIGFjY2VzcyBpdHMgZnVuY3Rpb25hbGl0eS4gDQoNCioqTk9URSoqOiBJZiB5b3UgcmVjZWl2ZSBhIFNTTCBvciBDQSBDZXJ0aWZpY2F0ZSBlcnJvciwgeW91IG1heSBuZWVkIHRvIHRha2UgdGhlIGV4dHJhIHN0ZXAgZG9jdW1lbnRlZCBiZWxvdy4gIA0KDQpgYGANCiMgSWYgZGV2dG9vbHMgcGFja2FnZSBpcyBub3QgaW5zdGFsbGVkDQppbnN0YWxsLnBhY2thZ2VzKCJkZXZ0b29scyIsIGRlcGVuZGVuY2llcyA9IFRSVUUpDQoNCiMgTm93IGluc3RhbGwgYW5kIGxvYWQgZndzaW5hdA0KZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJhZGFtZHNtaXRoL2Z3c2luYXQiKQ0KbGlicmFyeSgiZndzaW5hdCIpDQoNCiMgSWYgeW91IHJlY2VpdmUgYSBTU0wgb3IgQ0EgQ2VydGlmaWNhdGUgZXJyb3INCmluc3RhbGwucGFja2FnZXMoImh0dHIiKQ0KbGlicmFyeSgiaHR0ciIpDQpzZXRfY29uZmlnKGNvbmZpZyhzc2xfdmVyaWZ5cGVlciA9IDBMKSkNCmRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiYWRhbWRzbWl0aC9md3NpbmF0IikNCmxpYnJhcnkoImZ3c2luYXQiKQ0KYGBgDQoNCiMgVGhlIGBmd3NpbmF0YCBwYWNrYWdlDQoNClRoaXMgcGFja2FnZXMgY3VycmVudGx5IGNvbnRhaW5zIGZ1bmN0aW9ucyB0bzoNCg0KMS4gcmV0cmlldmUgaU5hdHVyYWxpc3Qgb2JzZXJ2YXRpb25zIGZvciBhbnkgbnVtYmVyIG9mIGF2YWlsYWJsZSBVU0ZXUyBwcm9wZXJ0aWVzIChgaW5hdF9yZXRyaWV2ZWApLCB0aGUgdmFsaWQgbmFtZXMgb2Ygd2hpY2ggY2FuIGJlIG9idGFpbmVkIHdpdGggdGhlIGBmaW5kX3JlZnVnZXNgIGZ1bmN0aW9uOw0KMS4gdXBkYXRlIHByZXZpb3VzIHJldHJpZXZhbHMgb2Ygb2JzZXJ2YXRpb25zIChgaW5hdF91cGRhdGVgKTsgDQoxLiBleHBvcnQgdGhlIG9ic2VydmF0aW9ucyB0byBzZXBhcmF0ZSBzcHJlYWRzaGVldHMgYnkgVVNGV1MgcHJvcGVydHkgZm9yIGRpc3RyaWJ1dGlvbiAoYGluYXRfZXhwb3J0YCk7IGFuZA0KMS4gaGFydmVzdCBvYnNlcnZhdGlvbnMgb24gVVNGV1MgcHJvcGVydGllcyBpbnRvIHRoZSBbVVNGV1MgTldSUyBpTmF0dXJhbGlzdCBwcm9qZWN0XShodHRwczovL3d3dy5pbmF0dXJhbGlzdC5vcmcvcHJvamVjdHMvdXNmd3MtbmF0aW9uYWwtd2lsZGxpZmUtcmVmdWdlLXN5c3RlbSkgKGBpbmF0X2hhcnZlc3RgKSANCg0KIyBVc2luZyBgZndzaW5hdGANCg0KVGhlIGZpcnN0IHN0ZXAgdG8gdXNpbmcgYGZ3c2luYXRgIGlzIHRvIGdlbmVyYXRlIGEgbGlzdCBvZiBVU0ZXUyBwcm9wZXJ0aWVzIGZyb20gd2hpY2ggeW91J2QgbGlrZSB0byByZXRyaWV2ZSBvYnNlcnZhdGlvbnMuICBUaGlzIGNhbiBiZSBkb25lIHVzaW5nIHRoZSBgZmluZF9yZWZ1Z2VzYCBmdW5jdGlvbiwgd2hpY2ggYWNjZXB0cyBhcyBpbnB1dCBhIHZlY3RvciBvZiBzdHJpbmdzIHlvdSBleHBlY3Qgd2lsbCByZXR1cm4geW91ciBkZXNpcmVkIHJlZnVnZShzKS4gVGhlIGBmaW5kX3JlZnVnZXNgIGZ1bmN0aW9uIGlzIGNhc2UtaW5zZW5zaXRpdmUgYW5kIHdvcmtzIHdpdGggcGFydGlhbCBtYXRjaGVzLiAgRm9yIGV4YW1wbGUsIGlmIHdlJ3JlIGludGVyZXN0ZWQgaW4gcmV0cmlldmluZyBvYnNlcnZhdGlvbnMgb24gSGFycmlzIE5lY2ssIExveGFoYXRjaGVlLCBhbmQgQ2hhc3NhaG93aXR6a2EgTldScywgd2UgY2FuIGlucHV0IGEgdmVjdG9yIG9mIHN0cmluZ3Mgd2UgdGhpbmsgYXJlIGFkZXF1YXRlIHRvIHJldHVybiB0aG9zZSByZWZ1Z2VzOg0KDQpgYGB7cn0NCnJlZnMgPC0gYygibWVycml0dCIsICJsb3giLCAiY2hhc3MiKQ0KKHJlZnMgPC0gZmluZF9yZWZ1Z2VzKHJlZnMpKQ0KYGBgDQoNClN1Y2Nlc3MhIE5vdGUgdGhhdCBzb21lIHNlYXJjaGVzIHdpbGwgcmV0dXJuIG11bHRpcGxlIG1hdGNoZXMgdGhhdCB5b3UgbWF5IG5vdCBiZSBzZWVraW5nLiBJZiB3ZSdyZSBpbnRlcmVzdGVkIGluIEhhdGNoaWUgTldSLCBmb3IgZXhhbXBsZToNCg0KYGBge3J9DQooaGF0Y2hpZSA8LSBmaW5kX3JlZnVnZXMoImhhdGNoaWUiKSkNCmBgYA0KDQpUaHJlZSBtYXRjaGVzISAgTm8gd29ycmllcyEgV2UgY2FuIGVpdGhlciBzZWxlY3QgdGhlIG9uZSB3ZSB3YW50ICphZCBob2MqIG9yIHVzZSByZWd1bGFyIGV4cHJlc3Npb25zIHRvIG5hcnJvdyB0aGUgbWF0Y2hlczoNCg0KYGBge3J9DQojIyBOYXJyb3cgdGhlIHNlYXJjaCBhZnRlciB0aGUgZmFjdA0KKGhhdGNoaWUgPC0gaGF0Y2hpZVsxXSkNCg0KIyBOYXJyb3cgdGhlIHNlYXJjaCB3aXRoIHJlZ3VsYXIgZXhwcmVzc2lvbnMNCiMgSW4gdGhpcyBjYXNlLCByZXF1aXJlIHRoZSBwcm9wZXJ0eSB0byBzdGFydCB3aXRoICJIYXRjaGllIg0KKGhhdGNoaWUgPC0gZmluZF9yZWZ1Z2VzKCJeaGF0Y2hpZSIpKQ0KYGBgDQoNClRoZXJlIGFyZSBhIGNvdXBsZSBtb3JlIG9wdGlvbnMgdG8gdGhlIGBmaW5kX3JlZnVnZXNgIGZ1bmN0aW9uIHRvIGhlbHAgeW91IG5hcnJvdyB5b3VyIHNlYXJjaDsgdXNlIGA/ZmluZF9yZWZ1Z2VzYCBmb3IgbW9yZSBpbmZvcm1hdGlvbi4gQnkgZGVmYXVsdCwgYGZpbmRfcmVmdWdlc2AgcmV0dXJucyAqKkFMTCoqIGF2YWlsYWJsZSBVU0ZXUyBwcm9wZXJ0aWVzLg0KDQpOb3cgbGV0J3MgcGljayBhIHJlZnVnZSBhbmQgcmV0cmlldmUgdGhlIG9ic2VydmF0aW9ucyBmcm9tIGlOYXR1cmFsaXN0Li4uIGBpbmF0X3JldHJpZXZlYCBpcyBvdXIgZnJpZW5kLiBCZWZvcmUgd2UgZG8gc28sIGhvd2V2ZXIsIHdlIG5lZWQgdG8gZGVjaWRlIG9uIGEgZmV3IG9wdGlvbnM6DQoNCjEuIERvIHdlIHdhbnQgYWxsIGlOYXR1cmFsaXN0IG9ic2VydmF0aW9ucyBvbiB0aGUgcHJvcGVydHkgb3Igb25seSB0aG9zZSBmcm9tIGEgc3BlY2lmaWMgcHJvamVjdD8NCg0KICAgIEJ5IGRlZmF1bHQsIGBpbmF0X3JldHJpZXZlYCByZXR1cm5zIG9ubHkgb2JzZXJ2YXRpb25zIGFzc29jaWF0ZWQgd2l0aCB0aGUgW1VTRldTIE5hdGlvbmFsIFdpbGRsaWZlIFJlZnVnZSBTeXN0ZW0gaU5hdHVyYWxpc3QgcHJvamVjdF0oaHR0cHM6Ly93d3cuaW5hdHVyYWxpc3Qub3JnL3Byb2plY3RzL3VzZndzLW5hdGlvbmFsLXdpbGRsaWZlLXJlZnVnZS1zeXN0ZW0pLiBJZiB5b3Ugd2FudCBhIGRpZmZlcmVudCBwcm9qZWN0LCB5b3UnbGwgbmVlZCB0byBzcGVjaWZ5IHdoaWNoIG9uZSB3aXRoIHRoZSBgaW5hdF9wcm9qYCBhcmd1bWVudCAoc2VlIGA/aW5hdF9yZXRyaWV2ZWAgZm9yIGd1aWRhbmNlIG9uIHdoYXQncyBleHBlY3RlZCkuICBJZiB5b3Ugd2FudCAqKkFMTCoqIG9ic2VydmF0aW9ucywgdXNlIGBpbmF0X3Byb2ogPSBOVUxMYCAoc2VlIGJlbG93KS4NCiANCjEuIERvIHdlIHdhbnQgdG8gbGltaXQgdGhlIGRhdGVzIG9mIHRob3NlIG9ic2VydmF0aW9ucz8NCg0KICAgIEJ5IGRlZmF1bHQsIGBpbmF0X3JldHJpZXZlYCBkb2VzIG5vdCBsaW1pdCByZXRyaWV2YWxzIGJ5IGRhdGUgcmFuZ2UuIFNlZSBgP2luYXRfcmV0cmlldmVgIGZvciBndWlkYW5jZSBpZiB0aGlzIGlzIG9mIGludGVyZXN0Lg0KDQoxLiBEbyB3ZSB3YW50IGhlbHBmdWwgbWVzc2FnZXMgZHVyaW5nIHRoZSByZXRyaWV2YWw/DQoNCiAgICBHZW5lcmFsbHkgdGhlc2UgYXJlIHVzZWZ1bCB0byB0cmFjayBwcm9ncmVzcywgc28gdGhlIGRlZmF1bHQgaXMgdG8gcHJpbnQgdGhlc2UgbWVzc2FnZXMuICBJZiB0aGV5IGFubm95IHlvdSwgeW91IGNhbiByZWR1Y2UgdGhlbSB0byBhIG1pbmltdW0gYnkgcGFzc2luZyBgdmVyYm9zZSA9IEZBTFNFYCB0byBgaW5hdF9yZXRyaWV2ZWAuDQoNCiMjIEV4YW1wbGU6IEhhcnJpcyBOZWNrIE5XUg0KDQpgYGB7cn0NCmhuIDwtIGZpbmRfcmVmdWdlcygiaGFycmlzIG5lY2siKQ0KDQojIEhhcnJpcyBOZWNrIHJlY29yZHMgT05MWSBvbiBVU0ZXUyBOV1JTIGlOYXR1cmFsaXN0IFByb2plY3QNCmhuX3JlY3MgPC0gaW5hdF9yZXRyaWV2ZShobikNCg0KIyBIYXJyaXMgTmVjayByZWNvcmRzIGFjcm9zcyBBTEwgaU5hdHVyYWxpc3QgUHJvamVjdHMNCmhuX2FsbCA8LSBpbmF0X3JldHJpZXZlKGhuLCBpbmF0X3Byb2ogPSBOVUxMKQ0KYGBgDQoNCk5vdGljZSB0aGVyZSB3ZXJlICoqMjMqKiBvYnNlcnZhdGlvbnMgb24gdGhlIHJlZnVnZSB0aGF0IGRvIG5vdCBiZWxvbmcgdG8gdGhlIFVTRldTIE5XUlMgcHJvamVjdC4gIFdlIHdhbnQgdGhvc2Ugb2JzZXJ2YXRpb25zISBXZSBjYW4gdHJ5IGFuZCBoYXJ2ZXN0IHRoZW0gd2l0aCBgaW5hdF9oYXJ2ZXN0YC4NCg0KVGhpcyB3aWxsIHJlcXVpcmUgeW91IHRvIGhhdmUgYW4gW2lOYXR1cmFsaXN0XShodHRwOi8vd3d3LmluYXR1cmFsaXN0Lm9yZy8pIGFjY291bnQgYW5kIHRvIHBhc3MgeW91ciB1c2VybmFtZSBhbmQgcGFzc3dvcmQuICBJIGlsbHVzdHJhdGUgaXQgaGVyZSB3aGlsZSBrZWVwaW5nIG15IGNyZWRlbnRpYWxzIGhpZGRlbiwgYnV0IHlvdSBjYW4gcGFzcyB5b3VyIHVzZXJuYW1lIGFuZCBwYXNzd29yZCBhcyBjaGFyYWN0ZXIgc3RyaW5ncyB0byB0aGUgYHVzZXJgIGFuZCBgcHdgIGFyZ3VtZW50cyBvZiB0aGUgZnVuY3Rpb24uLi4NCg0KYGBge3J9DQpobl9oYXJ2ZXN0IDwtIGluYXRfaGFydmVzdChobiwgdXNlciA9IFN5cy5nZXRlbnYoInVzZXIiKSwgcHcgPSBTeXMuZ2V0ZW52KCJwdyIpLCBpbnRlcmFjdGl2ZSA9IEZBTFNFKQ0KYGBgDQoNCk9jY2FzaW9uYWxseSBvYnNlcnZhdGlvbnMgY2Fubm90IGJlIGhhcnZlc3RlZCBiZWNhdXNlIHRoZSB1c2VyIHJlc3RyaWN0cyB0aGVpciBvYnNlcnZhdGlvbnMuICBJZiB0aGlzIGhhcHBlbnMsIHlvdSB3aWxsIHJlY2VpdmUgYSBtZXNzYWdlIGluZGljYXRpbmcgaG93IG1hbnkgcmVjb3JkcyBjb3VsZCBub3QgYmUgaGFydmVzdGVkLiBJbiB0aGUgY2FzZSBvZiBIYXJyaXMgTmVjaywgd2UgZG8gbm90IHJlY2VpdmUgc3VjaCBhIG1lc3NhZ2UgYmVjYXVzZSBhbGwgcmVjb3JkcyB3ZXJlIHN1Y2Nlc3NmdWxseSBoYXJ2ZXN0ZWQuIA0KDQpJZiBzb21lIHJlY29yZHMgY2Fubm90IGJlIGhhcnZlc3RlZCwgaG93ZXZlciwgeW91IGNhbiBleHBsb3JlIHRoZSByZWFzb25zIGZvciB0aGVzZSBmYWlsdXJlcyBieSAndGFibGluZycgdGhlIGVycm9yIG1lc3NhZ2UgY29sdW1uIG9mIHRoZSBvYmplY3QgeW91IGp1c3QgY3JlYXRlZC4gDQoNCmBgYHtyfQ0KIyBUaGlzIGRvZXNuJ3QgbWFrZSBzZW5zZSBmb3IgSGFycmlzIE5lY2ssIGJ1dCBoZXJlJ3MgaG93IHlvdSdkIGxvb2sgYXQgcmVhc29ucyBhbnkgcmVjb3Jkcw0KIyB3ZXJlIG5vdCBoYXJ2ZXN0ZWQgdG8gdGhlIFVTRldTIE5XUlMgcHJvamVjdC4uLg0KIyB0YWJsZShobl9oYXJ2ZXN0JGVycm9yX21zZykNCg0KIyBVU0ZXUyBOV1JTIHByb2plY3Qgbm93IGNvbnRhaW5zIGFsbCBjdXJyZW50bHkgaGFydmVzdGFibGUgb2JzZXJ2YXRpb25zDQpobl9yZWNzIDwtIGluYXRfcmV0cmlldmUoaG4pDQpgYGANCg0KTm90aWNlIHRoYXQgYWxsIDU1IHJlY29yZHMgb24gSGFycmlzIE5lY2sgYXJlIG5vdyBpbiB0aGUgVVNGV1MgTldSUyBwcm9qZWN0Li4uDQoNClRyeSBpdCB5b3Vyc2VsZiBmb3IgU2F2YW5uYWggTldSICgxMjYgcmVjb3JkcyBhdmFpbGFibGUgZm9yIGhhcnZlc3Qgd2hlbiBJIGxhc3QgY2hlY2tlZCkgb3Igc29tZSBvdGhlciByZWZ1Z2UuLi4gIE5vdGUgdGhhdCByZWNvcmQgaGFydmVzdGluZyB0YWtlcyBiZXR3ZWVuIDEtMiBzZWNvbmRzIHBlciByZWNvcmQsIHNvIHBsYW4gYWNjb3JkaW5nbHkgZm9yIGxhcmdlIGhhcnZlc3RzLg0KDQpgYGB7cn0NCnNhdiA8LSBmaW5kX3JlZnVnZXMoInNhdmFubmFoIikNCmBgYA0K