Habitat Configuration
The spatial distribution of habitat features can have many direct impacts on the way organisms occupy and move across a landscape. In this topic, we will provide a brief overview of how we can quantify habitat types and insert their metrics into our data workflows.
What is a Landscape?
Ecologists Perspective
A landscape is a mosaic of interacting ecosystems which are heterogeneous in at least one factor of interest.
Organism Perspective
The spatial distribution of suitable habitat.
We can think of landscapes being described in the following categories of increasing complexity
- Landscape Type - Categories of landscape features (binary)
- Landscape Composition - Relative abundance of landscape features (continuous)
- Landscape Configuration - Spatial arrangement of of landscape features (raster)
So let’s grab some raw data and look at it
Raw Data
For this exercise we are going to be looking at the area around the Rice Rivers Center using the national
The Multi-Resolution Land Characteristics Consortium mrlc.gov maintains a spatial database of land cover types for the US. If you were to download these in their entirety, they are huge and you’d spend quite a bit of time doing the “download \(\to\) open in R \(\to\) define boundary box \(\to\) crop and save” kind of stuff. Fortunately, there is an interactive viewer and download tool at https://www.mrlc.gov/viewer/ that works just fine for us.
Here we are going to work with land-cover types and for this, I’m taking a 25km\(^2\) box that includes the Rice Rivers Center. The data we get are in the form of a GeoTIFF, which we’ve used before in the rasters lecture. The interface to get the data is one that has you specify the are and then submit a request with your email to get a download. Depending upon the size of your request and the current traffic, it may take a while to get the rasters. When you do they’ll send you a link to a zip file you can grab.
For our purposes, I uploaded the land cover data from 2016 to Github and it can be found at the following url.
library( raster )
land_2016_url <- "https://github.com/dyerlab/ENVS-Lectures/raw/master/data/NLCD_2016_Land_Cover_L48_20190424_qn2B1f8ganicJNKnJN0e.tiff"
land_2016 <- raster( land_2016_url )
land_2016
Notice the following from the output:
- The raster has 6806 pixels (almost square…)
- The CRS has units of meters
- The resolution of each pixel is 30x30.
and it looks like:
plot( land_2016 )
The actual values of the raster include the following categories.
values( land_2016 ) %>%
table()
This is not uncommon. Whomever decided to make this raster has defined some kind of numbering scheme for each pixel. In the Zip file that you get form NLCD, there is a CSV file that has the legend. I’ve also uploaded that one, so lets go grab this and do a left_join()
on this baby!
legend_url <- "https://raw.githubusercontent.com/dyerlab/ENVS-Lectures/master/data/NLCD_landcover_legend_2018_12_17_qn2B1f8ganicJNKnJN0e.csv"
legend <- read_csv( legend_url )
If you look at the data you’ll see that out of the 17 entries, 0 of them are NA
. Again, I’m sure there is some good reason why this is the case, it is just not obvious to me at this time what that may be other than historic reasons.
legend %>%
filter( !is.na(Legend))
Describing Landscapes - Feature Types
From above, we defined three kinds of landscape analyses, the first of which was a listing of the types of features on landscape.
values( land_2016 ) %>%
table() %>%
as_tibble() -> landscape_types
landscape_types
So the table()
function does not really do much in the way of naming stuff, so let’s fix these.
names( landscape_types ) <- c("Value", "Frequency")
landscape_types
Now we can reach back to our relational data and use the Value
column as the foreign key into the data from the legend where Value
is the Primary Key
.
legend %>%
left_join( landscape_types, by="Value")
Ah yesss. If we look at the Value
in the landscape types, it is (for some reason) a character
representation! Let’s fix that and then make another run at it.
landscape_types$Value <- as.numeric(landscape_types$Value)
legend %>%
left_join( landscape_types, by="Value") %>%
filter( !is.na(Frequency) ) -> landscape_types
unique(landscape_types$Legend)
Describing Landscapes - Feature Composition
The next level of description is to examine the relative frequency of each of these habitat types on the landscape. Let’s grab the data and summarize it by the relative frequencies of the present types.
landscape_types %>%
mutate( Cover = Frequency / sum(!is.na(values(land_2016))) ) %>%
select( Legend, Value, Frequency, Cover) %>%
arrange( -Cover ) %>%
knitr::kable() %>%
kableExtra::kable_material_dark(full_width = FALSE )
Describing Landscapes - Spatial Configuration
There are a lot more metrics that we can estimate from landscape features than just identity and frequency and what we will be covering here is just a bit off the top. We can loosely categorize the kinds of metrics into the following groups:
- Aggregation metrics
- Area & Edge metrics
- Contrast metrics
- Core area metrics
- Diversity metrics
- Isolation metrics
- Shape metrics
- Subdivision metrics
We are mostly going to focus on Area & Edge as well as Diversity metrics here for simplicity. If you take a course in Landscape Ecology, you can get into the other types. I’m going to use the landscapemetrics
library (if you do not have it, then install it using install.packages()
).
There are some requirements for your raster to use this library:
The distance units of your projection are meter, as the package converts units internally and returns results in either meters, square meters or hectares. For more information see the help file of each function.
Your raster encodes landscape classes as integers (1, 2, 3, 4, …, n).
Landscape metrics describe categorical landscapes, that means that your landscape needs to be classified (we throw a warning if you have more than 30 classes to make sure you work with a classified landscape).
There is a check_landscape()
function that you can use and find out. Here is a raster that is not good.
library( landscapemetrics )
check_landscape( landscapemetrics::podlasie_ccilc )
Fortunately, this library was made with the NLCD data in mind, so ours works just fine.
check_landscape( land_2016 )
We can examine landscape metrics at several different nested scales, the landscape at a whole, the patch (spatially contiguous habitat types), and the class level (all values for “Barren Land” for example). For the landscape level, the following metrics are available.
list_lsm(level="landscape")
At the patch level, the following metrics are available.
list_lsm(level="patch")
And for each class of feature, the following are available.
list_lsm(level="class")
Take a look through these metric types for each level. For each of these metrics, there is a function whose names has the following configuration
lsm_{l|p|c}_metric()
where {l|p|c}
is one of l
, p
, or c
and the metric is the name as shown above. So, for example, if we are looking at the area of each patch in the landscape we would use the function
lsm_p_area( land_2016 )
As you see, it divides the landscape into individual patches, numbers them, and provides metric feedback. Let’s summarize for mixed (type=43) and evergreen (type = 42) forests.
lsm_p_area( land_2016 ) %>%
filter( class %in% c(42, 43) ) %>%
mutate( Forest = ifelse( class == 42, "Evergreen", "Mixed Hardwoods") ) %>%
ggplot( aes(value) ) +
geom_density() +
facet_grid( Forest ~ .) +
xlab("Area (ha)") +
ylab("Frequency") +
ggtitle("Area Distribution of Mixed Forest Types")
Other metrics you may be interested in include (use View( list_lsm())
to see them all):
gyrate - The mean distance between cells within it which approximates something like the average distance an organism can move within a patch before encountering a boundary.
ca - The physical area associated with each landscape type.
clump - A measure of how spatially contingent the features are ranging from random (no clumping) to all in one large patch (fully clumped).
pr - Patch richness.
shdi - Shannon’s Diversity index
Finding Patches
To connect back to the spatial arrangement of features, we can use the following functions to point out where specific patches may be on the original landscape. For example, we can plot the spatial location of the most common types (say the first 3) of habitat as:
lsm_p_area( land_2016 ) %>%
group_by( class ) %>%
summarize( `Area` = sum( value) ) %>%
arrange(-Area)
Which turn out to
plot( get_patches( land_2016, class=43)[[1]] )
plot( get_patches( land_2016, class=90)[[1]] )
plot( get_patches( land_2016, class=43)[[1]] )
The difference in the colors of these patches reflect the patch ID number that lsm_*
give them.
LS0tCnRpdGxlOiAiU3BhdGlhbCBIYWJpdGF0IEFuYWx5c2lzIGluIFIiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQpsaWJyYXJ5KCB0aWR5dmVyc2UgKQpsaWJyYXJ5KCByYXN0ZXIgKQpnZ3Bsb3QyOjp0aGVtZV9zZXQoIHRoZW1lX2J3KCApKQpgYGAKCgoKIyMgSGFiaXRhdCBDb25maWd1cmF0aW9uCgpUaGUgc3BhdGlhbCBkaXN0cmlidXRpb24gb2YgaGFiaXRhdCBmZWF0dXJlcyBjYW4gaGF2ZSBtYW55IGRpcmVjdCBpbXBhY3RzIG9uIHRoZSB3YXkgb3JnYW5pc21zIG9jY3VweSBhbmQgbW92ZSBhY3Jvc3MgYSBsYW5kc2NhcGUuICBJbiB0aGlzIHRvcGljLCB3ZSB3aWxsIHByb3ZpZGUgYSBicmllZiBvdmVydmlldyBvZiBob3cgd2UgY2FuIHF1YW50aWZ5IGhhYml0YXQgdHlwZXMgYW5kIGluc2VydCB0aGVpciBtZXRyaWNzIGludG8gb3VyIGRhdGEgd29ya2Zsb3dzLgoKCgoKCiMjIyBXaGF0IGlzIGEgTGFuZHNjYXBlPwoKRWNvbG9naXN0cyBQZXJzcGVjdGl2ZQoKPiBBIGxhbmRzY2FwZSBpcyBhIG1vc2FpYyBvZiBpbnRlcmFjdGluZyBlY29zeXN0ZW1zIHdoaWNoIGFyZSBoZXRlcm9nZW5lb3VzIGluIGF0IGxlYXN0IG9uZSBmYWN0b3Igb2YgaW50ZXJlc3QuCgpPcmdhbmlzbSBQZXJzcGVjdGl2ZQoKPiBUaGUgc3BhdGlhbCBkaXN0cmlidXRpb24gb2Ygc3VpdGFibGUgaGFiaXRhdF5bQSBwYXJ0aWN1bGFyIHNlY3Rpb24gb2YgbGFuZCBtYXkgcmVwcmVzZW50IG1hbnkgZGlmZmVybnQgJ2xhbmRzY2FwZXMnIHdoZW4gdGFrZW4gZnJvbSB0aGUgcGVyc3BlY3RpdmUgb2YgZGlmZmVyZW50IG9yZ2FuaXNtcy5dLgoKCgpXZSBjYW4gdGhpbmsgb2YgbGFuZHNjYXBlcyBiZWluZyBkZXNjcmliZWQgaW4gdGhlIGZvbGxvd2luZyBjYXRlZ29yaWVzIG9mIGluY3JlYXNpbmcgY29tcGxleGl0eQoKLSBMYW5kc2NhcGUgVHlwZSAtIENhdGVnb3JpZXMgb2YgbGFuZHNjYXBlIGZlYXR1cmVzIChiaW5hcnkpCi0gTGFuZHNjYXBlIENvbXBvc2l0aW9uIC0gUmVsYXRpdmUgYWJ1bmRhbmNlIG9mIGxhbmRzY2FwZSBmZWF0dXJlcyAoY29udGludW91cykKLSBMYW5kc2NhcGUgQ29uZmlndXJhdGlvbiAtIFNwYXRpYWwgYXJyYW5nZW1lbnQgb2Ygb2YgbGFuZHNjYXBlIGZlYXR1cmVzIChyYXN0ZXIpCgpTbyBsZXQncyBncmFiIHNvbWUgcmF3IGRhdGEgYW5kIGxvb2sgYXQgaXQgCgojIyMgUmF3IERhdGEKCgo8Y2VudGVyPgohW1RoZSBSaWNlIENlbnRlcl0oaHR0cHM6Ly9saXZlLnN0YXRpY2ZsaWNrci5jb20vNjU1MzUvNTA5OTg0OTgxNjlfMGU1MDJiYzdiYV9jX2QuanBnKQo8L2NlbnRlcj4KCkZvciB0aGlzIGV4ZXJjaXNlIHdlIGFyZSBnb2luZyB0byBiZSBsb29raW5nIGF0IHRoZSBhcmVhIGFyb3VuZCB0aGUgUmljZSBSaXZlcnMgQ2VudGVyIHVzaW5nIHRoZSBuYXRpb25hbCAKCgoKVGhlIE11bHRpLVJlc29sdXRpb24gTGFuZCBDaGFyYWN0ZXJpc3RpY3MgQ29uc29ydGl1bSBbbXJsYy5nb3ZdKGh0dHBzOi8vd3d3Lm1ybGMuZ292KSBtYWludGFpbnMgYSBzcGF0aWFsIGRhdGFiYXNlIG9mIGxhbmQgY292ZXIgdHlwZXMgZm9yIHRoZSBVUy4gIElmIHlvdSB3ZXJlIHRvIGRvd25sb2FkIHRoZXNlIGluIHRoZWlyIGVudGlyZXR5LCB0aGV5IGFyZSAqKmh1Z2UqKiBhbmQgeW91J2Qgc3BlbmQgcXVpdGUgYSBiaXQgb2YgdGltZSBkb2luZyB0aGUgImRvd25sb2FkICRcdG8kIG9wZW4gaW4gUiAkXHRvJCBkZWZpbmUgYm91bmRhcnkgYm94ICRcdG8kIGNyb3AgYW5kIHNhdmUiIGtpbmQgb2Ygc3R1ZmYuICBGb3J0dW5hdGVseSwgdGhlcmUgaXMgYW4gaW50ZXJhY3RpdmUgdmlld2VyIGFuZCBkb3dubG9hZCB0b29sIGF0IGh0dHBzOi8vd3d3Lm1ybGMuZ292L3ZpZXdlci8gdGhhdCB3b3JrcyBqdXN0IGZpbmUgZm9yIHVzLiAgCgohW21ybGMgaW50ZXJhY3RpdmUgdmllcndlcl0oaHR0cHM6Ly9saXZlLnN0YXRpY2ZsaWNrci5jb20vNjU1MzUvNTEwMTQyNDE2MjVfZjA4OTRlYzQzMl9jX2QuanBnKQoKSGVyZSB3ZSBhcmUgZ29pbmcgdG8gd29yayB3aXRoIGxhbmQtY292ZXIgdHlwZXMgYW5kIGZvciB0aGlzLCBJJ20gdGFraW5nIGEgMjVrbSReMiQgYm94IHRoYXQgaW5jbHVkZXMgdGhlIFtSaWNlIFJpdmVycyBDZW50ZXJdKGh0dHBzOi8vcmljZXJpdmVycy52Y3UuZWR1KS4gIFRoZSBkYXRhIHdlIGdldCBhcmUgaW4gdGhlIGZvcm0gb2YgYSBHZW9USUZGLCB3aGljaCB3ZSd2ZSB1c2VkIGJlZm9yZSBpbiB0aGUgW3Jhc3RlcnNdKGh0dHBzOi8vZHllcmxhYi5naXRodWIuaW8vRU5WUy1MZWN0dXJlcy9zcGF0aWFsL3Jhc3RlcnMvc2xpZGVzLmh0bWwjMSkgbGVjdHVyZS4gIFRoZSBpbnRlcmZhY2UgdG8gZ2V0IHRoZSBkYXRhIGlzIG9uZSB0aGF0IGhhcyB5b3Ugc3BlY2lmeSB0aGUgYXJlIGFuZCB0aGVuIHN1Ym1pdCBhIHJlcXVlc3Qgd2l0aCB5b3VyIGVtYWlsIHRvIGdldCBhIGRvd25sb2FkLiAgRGVwZW5kaW5nIHVwb24gdGhlIHNpemUgb2YgeW91ciByZXF1ZXN0IGFuZCB0aGUgY3VycmVudCB0cmFmZmljLCBpdCBtYXkgdGFrZSBhIHdoaWxlIHRvIGdldCB0aGUgcmFzdGVycy4gIFdoZW4geW91IGRvIHRoZXknbGwgc2VuZCB5b3UgYSBsaW5rIHRvIGEgemlwIGZpbGUgeW91IGNhbiBncmFiLgoKRm9yIG91ciBwdXJwb3NlcywgSSB1cGxvYWRlZCB0aGUgbGFuZCBjb3ZlciBkYXRhIGZyb20gMjAxNiB0byBHaXRodWIgYW5kIGl0IGNhbiBiZSBmb3VuZCBhdCB0aGUgZm9sbG93aW5nIHVybC4KCgpgYGB7cn0KbGlicmFyeSggcmFzdGVyICkKCmxhbmRfMjAxNl91cmwgPC0gImh0dHBzOi8vZ2l0aHViLmNvbS9keWVybGFiL0VOVlMtTGVjdHVyZXMvcmF3L21hc3Rlci9kYXRhL05MQ0RfMjAxNl9MYW5kX0NvdmVyX0w0OF8yMDE5MDQyNF9xbjJCMWY4Z2FuaWNKTktuSk4wZS50aWZmIgpsYW5kXzIwMTYgPC0gcmFzdGVyKCBsYW5kXzIwMTZfdXJsICkKbGFuZF8yMDE2CmBgYAoKTm90aWNlIHRoZSBmb2xsb3dpbmcgZnJvbSB0aGUgb3V0cHV0OiAgCi0gVGhlIHJhc3RlciBoYXMgNjgwNiBwaXhlbHMgKGFsbW9zdCBzcXVhcmUuLi4pICAKLSBUaGUgQ1JTIGhhcyB1bml0cyBvZiBtZXRlcnMgIAotIFRoZSByZXNvbHV0aW9uIG9mIGVhY2ggcGl4ZWwgaXMgMzB4MzAuCgphbmQgaXQgbG9va3MgbGlrZToKCmBgYHtyfQpwbG90KCBsYW5kXzIwMTYgKQpgYGAKCgpUaGUgYWN0dWFsIHZhbHVlcyBvZiB0aGUgcmFzdGVyIGluY2x1ZGUgdGhlIGZvbGxvd2luZyBjYXRlZ29yaWVzLgoKYGBge3J9CnZhbHVlcyggbGFuZF8yMDE2ICkgJT4lCiAgdGFibGUoKSAKYGBgCgpUaGlzIGlzIG5vdCB1bmNvbW1vbi4gV2hvbWV2ZXIgZGVjaWRlZCB0byBtYWtlIHRoaXMgcmFzdGVyIGhhcyBkZWZpbmVkIHNvbWUga2luZCBvZiBudW1iZXJpbmcgc2NoZW1lIGZvciBlYWNoIHBpeGVsLiAgSW4gdGhlIFppcCBmaWxlIHRoYXQgeW91IGdldCBmb3JtIE5MQ0QsIHRoZXJlIGlzIGEgQ1NWIGZpbGUgdGhhdCBoYXMgdGhlIGxlZ2VuZC4gIEkndmUgYWxzbyB1cGxvYWRlZCB0aGF0IG9uZSwgc28gbGV0cyBnbyBncmFiIHRoaXMgYW5kIGRvIGEgYGxlZnRfam9pbigpYCBvbiB0aGlzIGJhYnkhCgpgYGB7cn0KbGVnZW5kX3VybCA8LSAiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2R5ZXJsYWIvRU5WUy1MZWN0dXJlcy9tYXN0ZXIvZGF0YS9OTENEX2xhbmRjb3Zlcl9sZWdlbmRfMjAxOF8xMl8xN19xbjJCMWY4Z2FuaWNKTktuSk4wZS5jc3YiCmxlZ2VuZCA8LSByZWFkX2NzdiggbGVnZW5kX3VybCApCmBgYAoKSWYgeW91IGxvb2sgYXQgdGhlIGRhdGEgeW91J2xsIHNlZSB0aGF0IG91dCBvZiB0aGUgYHIgbnJvdyhsZWdlbmQpYCBlbnRyaWVzLCBgciBzdW0oaXMubmEobGVnZW5kJExlZ2VuZCkpYCBvZiB0aGVtIGFyZSBgTkFgLiAgQWdhaW4sIEknbSBzdXJlIHRoZXJlIGlzIHNvbWUgZ29vZCByZWFzb24gd2h5IHRoaXMgaXMgdGhlIGNhc2UsIGl0IGlzIGp1c3Qgbm90IG9idmlvdXMgdG8gbWUgYXQgdGhpcyB0aW1lIHdoYXQgdGhhdCBtYXkgYmUgb3RoZXIgdGhhbiBoaXN0b3JpYyByZWFzb25zXltJbiB0aGUgdmVyeSBvbGQgZGF5cyBvZiBjb21wdXRpbmcsIGxpa2Ugd2hlbiBJIHN0YXJ0ZWQsIHdlIGhhZCBzbyBsaXR0bGUgbWVtb3J5IG9uIHRoZSBjb21wdXRlcnMgdGhhdCB3ZSBoYWQgdG8gcGFjayBpbmZvcm1hdGlvbiBhcyB0aWdodGx5IGFzIHBvc3NpYmxlLCB3aGljaCBpcyB3aHkgdGhpbmdzIGxpa2UgbnVtYnJlciBuYW1pbmcgc2NoZW1lcyBhbmQgbG9vay11cCB0YWJsZXMgd2VyZSBuZWNlc3NhcnldLgoKYGBge3J9CmxlZ2VuZCAlPiUKICBmaWx0ZXIoICFpcy5uYShMZWdlbmQpKQoKYGBgCgojIyBEZXNjcmliaW5nIExhbmRzY2FwZXMgLSBGZWF0dXJlIFR5cGVzCgpGcm9tIGFib3ZlLCB3ZSBkZWZpbmVkIHRocmVlIGtpbmRzIG9mIGxhbmRzY2FwZSBhbmFseXNlcywgdGhlIGZpcnN0IG9mIHdoaWNoIHdhcyBhIGxpc3Rpbmcgb2YgdGhlIHR5cGVzIG9mIGZlYXR1cmVzIG9uIGxhbmRzY2FwZS4gIAoKCmBgYHtyfQp2YWx1ZXMoIGxhbmRfMjAxNiApICU+JQogIHRhYmxlKCkgJT4lIAogIGFzX3RpYmJsZSgpIC0+IGxhbmRzY2FwZV90eXBlcwpsYW5kc2NhcGVfdHlwZXMKYGBgCgpTbyB0aGUgYHRhYmxlKClgIGZ1bmN0aW9uIGRvZXMgbm90IHJlYWxseSBkbyBtdWNoIGluIHRoZSB3YXkgb2YgbmFtaW5nIHN0dWZmLCBzbyBsZXQncyBmaXggdGhlc2UuCgpgYGB7cn0KbmFtZXMoIGxhbmRzY2FwZV90eXBlcyApIDwtIGMoIlZhbHVlIiwgIkZyZXF1ZW5jeSIpCmxhbmRzY2FwZV90eXBlcwpgYGAKCk5vdyB3ZSBjYW4gcmVhY2ggYmFjayB0byBvdXIgcmVsYXRpb25hbCBkYXRhIGFuZCB1c2UgdGhlIGBWYWx1ZWAgY29sdW1uIGFzIHRoZSBmb3JlaWduIGtleSBpbnRvIHRoZSBkYXRhIGZyb20gdGhlIGxlZ2VuZCB3aGVyZSBgVmFsdWVgIGlzIHRoZSBgUHJpbWFyeSBLZXlgLgoKYGBge3J9CmxlZ2VuZCAlPiUKICBsZWZ0X2pvaW4oIGxhbmRzY2FwZV90eXBlcywgYnk9IlZhbHVlIikKYGBgCgpBaCB5ZXNzcy4gIElmIHdlIGxvb2sgYXQgdGhlIGBWYWx1ZWAgaW4gdGhlIGxhbmRzY2FwZSB0eXBlcywgaXQgaXMgKGZvciBzb21lIHJlYXNvbikgYSBgY2hhcmFjdGVyYCByZXByZXNlbnRhdGlvbiEgIExldCdzIGZpeCB0aGF0IGFuZCB0aGVuIG1ha2UgYW5vdGhlciBydW4gYXQgaXQuCgoKYGBge3J9CmxhbmRzY2FwZV90eXBlcyRWYWx1ZSA8LSBhcy5udW1lcmljKGxhbmRzY2FwZV90eXBlcyRWYWx1ZSkKbGVnZW5kICU+JQogIGxlZnRfam9pbiggbGFuZHNjYXBlX3R5cGVzLCBieT0iVmFsdWUiKSAlPiUKICBmaWx0ZXIoICFpcy5uYShGcmVxdWVuY3kpICkgLT4gbGFuZHNjYXBlX3R5cGVzCnVuaXF1ZShsYW5kc2NhcGVfdHlwZXMkTGVnZW5kKQpgYGAKCgoKIyMgRGVzY3JpYmluZyBMYW5kc2NhcGVzIC0gRmVhdHVyZSBDb21wb3NpdGlvbgoKVGhlIG5leHQgbGV2ZWwgb2YgZGVzY3JpcHRpb24gaXMgdG8gZXhhbWluZSB0aGUgcmVsYXRpdmUgZnJlcXVlbmN5IG9mIGVhY2ggb2YgdGhlc2UgaGFiaXRhdCB0eXBlcyBvbiB0aGUgbGFuZHNjYXBlLiAgTGV0J3MgZ3JhYiB0aGUgZGF0YSBhbmQgc3VtbWFyaXplIGl0IGJ5IHRoZSByZWxhdGl2ZSBmcmVxdWVuY2llcyBvZiB0aGUgcHJlc2VudCB0eXBlcy4KCgpgYGB7cn0KbGFuZHNjYXBlX3R5cGVzICU+JQogIG11dGF0ZSggQ292ZXIgPSBGcmVxdWVuY3kgLyBzdW0oIWlzLm5hKHZhbHVlcyhsYW5kXzIwMTYpKSkgKSAlPiUKICBzZWxlY3QoIExlZ2VuZCwgVmFsdWUsIEZyZXF1ZW5jeSwgQ292ZXIpICU+JQogIGFycmFuZ2UoIC1Db3ZlciApICU+JQogIGtuaXRyOjprYWJsZSgpICU+JQogIGthYmxlRXh0cmE6OmthYmxlX21hdGVyaWFsX2RhcmsoZnVsbF93aWR0aCA9IEZBTFNFICkKYGBgCgojIyBEZXNjcmliaW5nIExhbmRzY2FwZXMgLSBTcGF0aWFsIENvbmZpZ3VyYXRpb24KClRoZXJlIGFyZSAqYSBsb3QqIG1vcmUgbWV0cmljcyB0aGF0IHdlIGNhbiBlc3RpbWF0ZSBmcm9tIGxhbmRzY2FwZSBmZWF0dXJlcyB0aGFuIGp1c3QgaWRlbnRpdHkgYW5kIGZyZXF1ZW5jeSBhbmQgd2hhdCB3ZSB3aWxsIGJlIGNvdmVyaW5nIGhlcmUgaXMganVzdCBhIGJpdCBvZmYgdGhlIHRvcC4gIFdlIGNhbiBsb29zZWx5IGNhdGVnb3JpemUgdGhlIGtpbmRzIG9mIG1ldHJpY3MgaW50byB0aGUgZm9sbG93aW5nIGdyb3VwczogIAoKLSBBZ2dyZWdhdGlvbiBtZXRyaWNzICAKLSBBcmVhICYgRWRnZSBtZXRyaWNzICAKLSBDb250cmFzdCBtZXRyaWNzICAKLSBDb3JlIGFyZWEgbWV0cmljcyAgCi0gRGl2ZXJzaXR5IG1ldHJpY3MgIAotIElzb2xhdGlvbiBtZXRyaWNzICAKLSBTaGFwZSBtZXRyaWNzICAKLSBTdWJkaXZpc2lvbiBtZXRyaWNzCgpXZSBhcmUgbW9zdGx5IGdvaW5nIHRvIGZvY3VzIG9uIEFyZWEgJiBFZGdlIGFzIHdlbGwgYXMgRGl2ZXJzaXR5IG1ldHJpY3MgaGVyZSBmb3Igc2ltcGxpY2l0eS4gIElmIHlvdSB0YWtlIGEgY291cnNlIGluIExhbmRzY2FwZSBFY29sb2d5LCB5b3UgY2FuIGdldCBpbnRvIHRoZSBvdGhlciB0eXBlcy4gSSdtIGdvaW5nIHRvIHVzZSB0aGUgYGxhbmRzY2FwZW1ldHJpY3NgIGxpYnJhcnkgKGlmIHlvdSBkbyBub3QgaGF2ZSBpdCwgdGhlbiBpbnN0YWxsIGl0IHVzaW5nIGBpbnN0YWxsLnBhY2thZ2VzKClgKS4KClRoZXJlIGFyZSBzb21lIHJlcXVpcmVtZW50cyBmb3IgeW91ciByYXN0ZXIgdG8gdXNlIHRoaXMgbGlicmFyeTogIAoKMS4gVGhlIGRpc3RhbmNlIHVuaXRzIG9mIHlvdXIgcHJvamVjdGlvbiBhcmUgbWV0ZXIsIGFzIHRoZSBwYWNrYWdlIGNvbnZlcnRzIHVuaXRzIGludGVybmFsbHkgYW5kIHJldHVybnMgcmVzdWx0cyBpbiBlaXRoZXIgbWV0ZXJzLCBzcXVhcmUgbWV0ZXJzIG9yIGhlY3RhcmVzLiBGb3IgbW9yZSBpbmZvcm1hdGlvbiBzZWUgdGhlIGhlbHAgZmlsZSBvZiBlYWNoIGZ1bmN0aW9uLiAgCgoyLiBZb3VyIHJhc3RlciBlbmNvZGVzIGxhbmRzY2FwZSBjbGFzc2VzIGFzIGludGVnZXJzICgxLCAyLCAzLCA0LCDigKYsIG4pLiAgCgozLiBMYW5kc2NhcGUgbWV0cmljcyBkZXNjcmliZSBjYXRlZ29yaWNhbCBsYW5kc2NhcGVzLCB0aGF0IG1lYW5zIHRoYXQgeW91ciBsYW5kc2NhcGUgbmVlZHMgdG8gYmUgY2xhc3NpZmllZCAod2UgdGhyb3cgYSB3YXJuaW5nIGlmIHlvdSBoYXZlIG1vcmUgdGhhbiAzMCBjbGFzc2VzIHRvIG1ha2Ugc3VyZSB5b3Ugd29yayB3aXRoIGEgY2xhc3NpZmllZCBsYW5kc2NhcGUpLiAgCgpUaGVyZSBpcyBhIGBjaGVja19sYW5kc2NhcGUoKWAgZnVuY3Rpb24gdGhhdCB5b3UgY2FuIHVzZSBhbmQgZmluZCBvdXQuICBIZXJlIGlzIGEgcmFzdGVyIHRoYXQgaXMgbm90IGdvb2QuCgpgYGB7cn0KbGlicmFyeSggbGFuZHNjYXBlbWV0cmljcyApCmNoZWNrX2xhbmRzY2FwZSggbGFuZHNjYXBlbWV0cmljczo6cG9kbGFzaWVfY2NpbGMgKQpgYGAKCkZvcnR1bmF0ZWx5LCB0aGlzIGxpYnJhcnkgd2FzIG1hZGUgd2l0aCB0aGUgTkxDRCBkYXRhIGluIG1pbmQsIHNvIG91cnMgd29ya3MganVzdCBmaW5lLgoKYGBge3J9CmNoZWNrX2xhbmRzY2FwZSggbGFuZF8yMDE2ICkKYGBgCgpXZSBjYW4gZXhhbWluZSBsYW5kc2NhcGUgbWV0cmljcyBhdCBzZXZlcmFsIGRpZmZlcmVudCBuZXN0ZWQgc2NhbGVzLCB0aGUgbGFuZHNjYXBlIGF0IGEgd2hvbGUsIHRoZSBwYXRjaCAoc3BhdGlhbGx5IGNvbnRpZ3VvdXMgaGFiaXRhdCB0eXBlcyksIGFuZCB0aGUgY2xhc3MgbGV2ZWwgKGFsbCB2YWx1ZXMgZm9yICJCYXJyZW4gTGFuZCIgZm9yIGV4YW1wbGUpLiAgRm9yIHRoZSBsYW5kc2NhcGUgbGV2ZWwsIHRoZSBmb2xsb3dpbmcgbWV0cmljcyBhcmUgYXZhaWxhYmxlLgoKYGBge3J9Cmxpc3RfbHNtKGxldmVsPSJsYW5kc2NhcGUiKQpgYGAKCkF0IHRoZSBwYXRjaCBsZXZlbCwgdGhlIGZvbGxvd2luZyBtZXRyaWNzIGFyZSBhdmFpbGFibGUuCgpgYGB7cn0KbGlzdF9sc20obGV2ZWw9InBhdGNoIikKYGBgCgoKQW5kIGZvciBlYWNoIGNsYXNzIG9mIGZlYXR1cmUsIHRoZSBmb2xsb3dpbmcgYXJlIGF2YWlsYWJsZS4KCmBgYHtyfQpsaXN0X2xzbShsZXZlbD0iY2xhc3MiKQpgYGAKClRha2UgYSBsb29rIHRocm91Z2ggdGhlc2UgbWV0cmljIHR5cGVzIGZvciBlYWNoIGxldmVsLiAgRm9yIGVhY2ggb2YgdGhlc2UgbWV0cmljcywgdGhlcmUgaXMgYSBmdW5jdGlvbiB3aG9zZSBuYW1lcyBoYXMgdGhlIGZvbGxvd2luZyBjb25maWd1cmF0aW9uCgpgbHNtX3tsfHB8Y31fbWV0cmljKClgCgp3aGVyZSBge2x8cHxjfWAgaXMgb25lIG9mIGBsYCwgYHBgLCBvciBgY2AgYW5kIHRoZSBtZXRyaWMgaXMgdGhlIG5hbWUgYXMgc2hvd24gYWJvdmUuICBTbywgZm9yIGV4YW1wbGUsIGlmIHdlIGFyZSBsb29raW5nIGF0IHRoZSBhcmVhIG9mIGVhY2ggcGF0Y2ggaW4gdGhlIGxhbmRzY2FwZSB3ZSB3b3VsZCB1c2UgdGhlIGZ1bmN0aW9uCgpgYGB7cn0KbHNtX3BfYXJlYSggbGFuZF8yMDE2ICkgCmBgYAoKQXMgeW91IHNlZSwgaXQgZGl2aWRlcyB0aGUgbGFuZHNjYXBlIGludG8gaW5kaXZpZHVhbCBwYXRjaGVzLCBudW1iZXJzIHRoZW0sIGFuZCBwcm92aWRlcyBtZXRyaWMgZmVlZGJhY2suIExldCdzIHN1bW1hcml6ZSBmb3IgbWl4ZWQgKHR5cGU9NDMpIGFuZCBldmVyZ3JlZW4gKHR5cGUgPSA0MikgZm9yZXN0cy4KCmBgYHtyfQpsc21fcF9hcmVhKCBsYW5kXzIwMTYgKSAlPiUKICBmaWx0ZXIoIGNsYXNzICVpbiUgYyg0MiwgNDMpICkgJT4lCiAgbXV0YXRlKCBGb3Jlc3QgPSBpZmVsc2UoIGNsYXNzID09IDQyLCAiRXZlcmdyZWVuIiwgIk1peGVkIEhhcmR3b29kcyIpICkgJT4lCiAgZ2dwbG90KCBhZXModmFsdWUpICkgKyAKICBnZW9tX2RlbnNpdHkoKSArIAogIGZhY2V0X2dyaWQoIEZvcmVzdCB+IC4pICsKICB4bGFiKCJBcmVhIChoYSkiKSArIAogIHlsYWIoIkZyZXF1ZW5jeSIpICsgCiAgZ2d0aXRsZSgiQXJlYSBEaXN0cmlidXRpb24gb2YgTWl4ZWQgRm9yZXN0IFR5cGVzIikKYGBgCgoKT3RoZXIgbWV0cmljcyB5b3UgbWF5IGJlIGludGVyZXN0ZWQgaW4gaW5jbHVkZSAodXNlIGBWaWV3KCBsaXN0X2xzbSgpKWAgdG8gc2VlIHRoZW0gYWxsKTogCgotICpneXJhdGUqIC0gVGhlIG1lYW4gZGlzdGFuY2UgYmV0d2VlbiBjZWxscyB3aXRoaW4gaXQgd2hpY2ggYXBwcm94aW1hdGVzIHNvbWV0aGluZyBsaWtlIHRoZSBhdmVyYWdlIGRpc3RhbmNlIGFuIG9yZ2FuaXNtIGNhbiBtb3ZlIHdpdGhpbiBhIHBhdGNoIGJlZm9yZSBlbmNvdW50ZXJpbmcgYSBib3VuZGFyeS4gIAoKLSAqY2EqIC0gVGhlIHBoeXNpY2FsIGFyZWEgYXNzb2NpYXRlZCB3aXRoIGVhY2ggbGFuZHNjYXBlIHR5cGUuCgoKLSAqY2x1bXAqIC0gQSBtZWFzdXJlIG9mIGhvdyBzcGF0aWFsbHkgY29udGluZ2VudCB0aGUgZmVhdHVyZXMgYXJlIHJhbmdpbmcgZnJvbSByYW5kb20gKG5vIGNsdW1waW5nKSB0byBhbGwgaW4gb25lIGxhcmdlIHBhdGNoIChmdWxseSBjbHVtcGVkKS4KCi0gKnByKiAtIFBhdGNoIHJpY2huZXNzLgoKLSAqc2hkaSogLSBTaGFubm9uJ3MgRGl2ZXJzaXR5IGluZGV4CgoKIyMgRmluZGluZyBQYXRjaGVzCgpUbyBjb25uZWN0IGJhY2sgdG8gdGhlIHNwYXRpYWwgYXJyYW5nZW1lbnQgb2YgZmVhdHVyZXMsIHdlIGNhbiB1c2UgdGhlIGZvbGxvd2luZyBmdW5jdGlvbnMgdG8gcG9pbnQgb3V0IHdoZXJlIHNwZWNpZmljIHBhdGNoZXMgbWF5IGJlIG9uIHRoZSBvcmlnaW5hbCBsYW5kc2NhcGUuICBGb3IgZXhhbXBsZSwgd2UgY2FuIHBsb3QgdGhlIHNwYXRpYWwgbG9jYXRpb24gb2YgdGhlIG1vc3QgY29tbW9uIHR5cGVzIChzYXkgdGhlIGZpcnN0IDMpIG9mIGhhYml0YXQgYXM6CgoKCmBgYHtyfQpsc21fcF9hcmVhKCBsYW5kXzIwMTYgKSAlPiUKICBncm91cF9ieSggY2xhc3MgKSAlPiUKICBzdW1tYXJpemUoIGBBcmVhYCA9IHN1bSggdmFsdWUpICkgJT4lCiAgYXJyYW5nZSgtQXJlYSkKYGBgCgpXaGljaCB0dXJuIG91dCB0byAKCmBgYHtyfQpwbG90KCBnZXRfcGF0Y2hlcyggbGFuZF8yMDE2LCBjbGFzcz00MylbWzFdXSApIApgYGAKCgpgYGB7cn0KcGxvdCggZ2V0X3BhdGNoZXMoIGxhbmRfMjAxNiwgY2xhc3M9OTApW1sxXV0gKSAKYGBgCgpgYGB7cn0KcGxvdCggZ2V0X3BhdGNoZXMoIGxhbmRfMjAxNiwgY2xhc3M9NDMpW1sxXV0gKSAKYGBgCgpUaGUgZGlmZmVyZW5jZSBpbiB0aGUgY29sb3JzIG9mIHRoZXNlIHBhdGNoZXMgcmVmbGVjdCB0aGUgcGF0Y2ggSUQgbnVtYmVyIHRoYXQgYGxzbV8qYCBnaXZlIHRoZW0u